1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8054307
  27  * @summary Tests correctness of string related intrinsics and C2 optimizations.
  28  *
  29  * @run main/timeout=240 compiler.intrinsics.string.TestStringIntrinsics
  30  */
  31 
  32 package compiler.intrinsics.string;
  33 
  34 import java.lang.annotation.ElementType;
  35 import java.lang.annotation.Retention;
  36 import java.lang.annotation.RetentionPolicy;
  37 import java.lang.annotation.Target;
  38 import java.lang.reflect.Method;
  39 import java.util.Arrays;
  40 
  41 public class TestStringIntrinsics {
  42 
  43     public enum Operation {
  44         ARR_EQUALS_B, ARR_EQUALS_C, EQUALS, COMPARE_TO, INDEX_OF, INDEX_OF_CON_U, INDEX_OF_CON_L,
  45         INDEX_OF_CON_UL, CONCAT, CONCAT_C, CONCAT_I, CONCAT_M, INDEX_OF_CHAR
  46     }
  47 
  48     @Retention(RetentionPolicy.RUNTIME)
  49     @Target(ElementType.METHOD)
  50     @interface Test {
  51         Operation op();
  52         String constString() default "";
  53         String[] inStrings() default {};
  54         char[] inChars() default {};
  55         int[] inInts() default {};
  56         String[] outStrings() default {};
  57     }
  58 
  59     public static void main(String[] args) throws Exception {
  60         new TestStringIntrinsics().run();
  61     }
  62 
  63     public void run() throws Exception {
  64         // Build latin1 and UTF16 strings
  65         StringBuilder latin1Builder = new StringBuilder();
  66         for (int i = 0; i <= 255; ++i) {
  67             latin1Builder.append((char) i);
  68         }
  69         String latin1 = latin1Builder.toString();
  70         StringBuilder utf16Builder = new StringBuilder();
  71         for (int i = 0; i <= 10000; ++i) {
  72             utf16Builder.append((char) i);
  73         }
  74         String utf16 = utf16Builder.toString();
  75 
  76         // Invoke test methods
  77         for (Method m : TestStringIntrinsics.class.getMethods()) {
  78             if (m.isAnnotationPresent(Test.class)) {
  79                 System.out.print("Checking " + m.getName() + "... ");
  80                 Operation op = m.getAnnotation(Test.class).op();
  81                 Test antn = m.getAnnotation(Test.class);
  82                 if (isStringConcatTest(op)) {
  83                     checkStringConcat(op, m, antn);
  84                 } else {
  85                     checkIntrinsics(op, m, latin1, utf16, antn);
  86                 }
  87                 System.out.println("Done.");
  88             }
  89         }
  90     }
  91 
  92     private boolean isStringConcatTest(Operation op) {
  93         return op == Operation.CONCAT ||
  94                op == Operation.CONCAT_C ||
  95                op == Operation.CONCAT_I ||
  96                op == Operation.CONCAT_M;
  97     }
  98 
  99     /**
 100      * Checks correctness of the String.equals, String.compareTo and String.indexOf intrinsics.
 101      * -XX:SpecialStringEquals
 102      * -XX:SpecialStringCompareTo
 103      * -XX:SpecialStringIndexOf
 104      */
 105     private void checkIntrinsics(Operation op, Method m, String latin1, String utf16, Test antn) throws Exception {
 106         for (int i = 0; i < 50_000; ++i) {
 107             // Copy and permute latin1 and UTF16 string
 108             char[] arrL = latin1.toCharArray();
 109             int indexL = i % arrL.length;
 110             int mod = (arrL.length - arrL[indexL]);
 111             int incL = i % ((mod != 0) ? mod : 1);
 112             arrL[indexL] = (char) ((int) arrL[indexL] + incL);
 113             String latin1Copy = String.valueOf(arrL);
 114 
 115             char[] arrU = utf16.toCharArray();
 116             int indexU = i % arrU.length;
 117             mod = (arrU.length - arrU[indexU]);
 118             int incU = i % ((mod != 0) ? mod : 1);
 119             arrU[indexU] = (char) ((int) arrU[indexU] + incU);
 120             String utf16Copy = String.valueOf(arrU);
 121 
 122             switch (op) {
 123             case ARR_EQUALS_B:
 124                 invokeAndCheck(m, (incL == 0), latin1.getBytes("ISO-8859-1"), latin1Copy.getBytes("ISO-8859-1"));
 125                 invokeAndCheck(m, true, new byte[] {1, 2, 3}, new byte[] {1, 2, 3});
 126                 invokeAndCheck(m, true, new byte[] {1}, new byte[] {1});
 127                 invokeAndCheck(m, true, new byte[] {}, new byte[] {});
 128                 break;
 129             case ARR_EQUALS_C:
 130                 invokeAndCheck(m, (incU == 0), utf16.toCharArray(), arrU);
 131                 break;
 132             case EQUALS:
 133                 invokeAndCheck(m, (incL == 0), latin1, latin1Copy);
 134                 invokeAndCheck(m, false, latin1, "");
 135                 invokeAndCheck(m, false, "", latin1);
 136 
 137                 invokeAndCheck(m, (incU == 0), utf16, utf16Copy);
 138                 invokeAndCheck(m, false, utf16, "");
 139                 invokeAndCheck(m, false, "", utf16);
 140 
 141                 invokeAndCheck(m, false, latin1, utf16);
 142                 break;
 143             case COMPARE_TO:
 144                 invokeAndCheck(m, -incL, latin1, latin1Copy);
 145                 invokeAndCheck(m, latin1.length(), latin1, "");
 146 
 147                 invokeAndCheck(m, -incU, utf16, utf16Copy);
 148                 invokeAndCheck(m, utf16.length(), utf16, "");
 149 
 150                 // Cross coder
 151                 char cL = latin1.charAt(indexL);
 152                 char cU = utf16.charAt(indexU);
 153                 invokeAndCheck(m, cL - cU, latin1, latin1.replace(cL, cU));
 154                 invokeAndCheck(m, cU - cL, utf16, utf16.replace(cU, cL));
 155 
 156                 // Different lengths
 157                 invokeAndCheck(m, 1, "ABCD", "ABC");
 158                 invokeAndCheck(m, -1, "\uff21\uff22\uff23", "\uff21\uff22\uff23\uff24");
 159                 invokeAndCheck(m, 1, "ABC\uff24", "ABC");
 160                 invokeAndCheck(m, 3, "ABC\uff24\uff25\uff26", "ABC");
 161                 invokeAndCheck(m, -1, "ABC","ABC\uff24");
 162                 invokeAndCheck(m, -3, "ABC","ABC\uff24\uff25\uff26");
 163                 break;
 164             case INDEX_OF:
 165                 invokeAndCheck(m, indexL, latin1, latin1.substring(indexL), (indexL > 42) ? 42 : 0);
 166                 invokeAndCheck(m, 0, latin1, "", 0);
 167 
 168                 invokeAndCheck(m, indexU, utf16, utf16.substring(indexU), (indexU > 42) ? 42 : 0);
 169                 invokeAndCheck(m, 0, utf16, "", 0);
 170 
 171                 // Cross coder
 172                 invokeAndCheck(m, -1, latin1.substring(0, indexL), utf16.substring(indexU), (indexL > 42) ? 42 : 0);
 173                 // Skip latin1 chars in utf16 string
 174                 int start = 256;
 175                 int end = indexU > start ? indexU : start;
 176                 invokeAndCheck(m, end-start, utf16.substring(start, end) + latin1.substring(indexL), latin1.substring(indexL), 0);
 177                 break;
 178             case INDEX_OF_CON_L:
 179                 invokeAndCheck(m, antn.constString(), latin1);
 180                 break;
 181             case INDEX_OF_CON_U:
 182                 invokeAndCheck(m, antn.constString(), utf16);
 183                 break;
 184             case INDEX_OF_CON_UL:
 185                 invokeAndCheck(m, antn.constString(), utf16);
 186                 break;
 187             case INDEX_OF_CHAR:
 188                 invokeAndCheck(m, 7, "abcdefg\uD800\uDC00", 65536, 0);
 189                 invokeAndCheck(m, -1, "abcdefg\uD800\uDC01", 65536, 0);
 190                 invokeAndCheck(m, -1, "abcdefg\uD800", 65536, 0);
 191                 invokeAndCheck(m, 3, "abc\u0107", 263, 0);
 192                 invokeAndCheck(m, -1, "abc\u0108", 263, 0);
 193                 invokeAndCheck(m, 7, "abcdefg\u0107", 263, 0);
 194                 invokeAndCheck(m, 7, "abcdefg\u0107", 263, -1);
 195                 invokeAndCheck(m, 0, "\u0107", 263, 0);
 196                 break;
 197             default:
 198                 throw new RuntimeException("Unexpected operation.");
 199             }
 200         }
 201     }
 202 
 203     /**
 204      * Checks correctness of the C2 string concatenation optimization.
 205      * -XX:OptimizeStringConcat
 206      */
 207     private void checkStringConcat(Operation op, Method m, Test antn) throws Exception {
 208         for (int i = 0; i < 50_000; ++i) {
 209             String[] result = antn.outStrings();
 210             switch(op) {
 211             case CONCAT:
 212                 String[] strs = antn.inStrings();
 213                 for (int j = 0; j < strs.length; ++j) {
 214                     invokeAndCheck(m, result[j], strs[j]);
 215                 }
 216                 break;
 217             case CONCAT_C:
 218                 char[] ch = antn.inChars();
 219                 for (int j = 0; j < ch.length; ++j) {
 220                     invokeAndCheck(m, result[j], ch[j]);
 221                 }
 222                 break;
 223             case CONCAT_I:
 224                 int[] k = antn.inInts();
 225                 for (int j = 0; j < k.length; ++j) {
 226                     invokeAndCheck(m, result[j], k[j]);
 227                 }
 228                 break;
 229             case CONCAT_M:
 230                 strs = antn.inStrings();
 231                 ch = antn.inChars();
 232                 k = antn.inInts();
 233                 for (int j = 0; j < strs.length; ++j) {
 234                     invokeAndCheck(m, result[j], strs[j], ch[j], k[j]);
 235                 }
 236                 break;
 237             default:
 238                 throw new RuntimeException("Unexpected operation.");
 239             }
 240         }
 241     }
 242 
 243     /**
 244      * Invokes method 'm' by passing arguments 'args' and checks if the
 245      * returned value equals 'expectedResult'.
 246      */
 247     private void invokeAndCheck(Method m, Object expectedResult, Object... args) throws Exception {
 248         Object result = m.invoke(null, args);
 249         if (!result.equals(expectedResult)) {
 250 //            System.out.println("Expected:");
 251 //            System.out.println(expectedResult);
 252 //            System.out.println("Returned:");
 253 //            System.out.println(result);
 254             throw new RuntimeException("Result of '" + m.getName() + "' not equal to expected value.");
 255         }
 256     }
 257 
 258     /*
 259      * Constants
 260      */
 261     static final char charU = '\uff21';
 262     static final char charL = 'A';
 263     static final String emptyString = "";
 264     static final String stringL = "abcdefghijklmnop";
 265     static final String stringSmallL = "abc";
 266     static final String stringU = "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28";
 267     static final String stringSmallU = "\u0f21\u0f22\u0f23";
 268     static final int constInt = 123;
 269     static final int constIntNeg = -123;
 270 
 271     /*
 272      * Arrays.equals
 273      */
 274     @Test(op = Operation.ARR_EQUALS_B)
 275     public static boolean arrayEqualsB(byte[] a, byte[] b) {
 276       return Arrays.equals(a, b);
 277     }
 278 
 279     @Test(op = Operation.ARR_EQUALS_C)
 280     public static boolean arrayEqualsC(char[] a, char[] b) {
 281       return Arrays.equals(a, b);
 282     }
 283 
 284     /*
 285      * String.equals
 286      */
 287     @Test(op = Operation.EQUALS)
 288     public static boolean equals(String a, String b) {
 289         return a.equals(b);
 290     }
 291 
 292     /*
 293      * String.compareTo
 294      */
 295     @Test(op = Operation.COMPARE_TO)
 296     public static int compareTo(String a, String b) {
 297         return a.compareTo(b);
 298     }
 299 
 300     /*
 301      * String.indexOf
 302      */
 303     @Test(op = Operation.INDEX_OF)
 304     public static int indexOf(String a, String b, int from) {
 305         return a.indexOf(b, from);
 306     }
 307 
 308     @Test(op = Operation.INDEX_OF_CON_U, constString = stringSmallU)
 309     public static String indexOfConstU(String a) {
 310         int result = a.indexOf(stringSmallU);
 311         return a.substring(result, result + stringSmallU.length());
 312     }
 313 
 314     @Test(op = Operation.INDEX_OF_CON_U, constString = stringU)
 315     public static String indexOfConstLargeU(String a) {
 316         int result = a.indexOf(stringU);
 317         return a.substring(result, result + stringU.length());
 318     }
 319 
 320     @Test(op = Operation.INDEX_OF_CON_U, constString = emptyString)
 321     public static String indexOfConstEmptyU(String a) {
 322         int result = a.indexOf(emptyString);
 323         return a.substring(result, result + emptyString.length());
 324     }
 325 
 326     @Test(op = Operation.INDEX_OF_CON_L, constString = stringSmallL)
 327     public static String indexOfConstL(String a) {
 328         int result = a.indexOf(stringSmallL);
 329         return a.substring(result, result + stringSmallL.length());
 330     }
 331 
 332     @Test(op = Operation.INDEX_OF_CON_L, constString = stringL)
 333     public static String indexOfConstLargeL(String a) {
 334         int result = a.indexOf(stringL);
 335         return a.substring(result, result + stringL.length());
 336     }
 337 
 338     @Test(op = Operation.INDEX_OF_CON_L, constString = emptyString)
 339     public static String indexOfConstEmptyL(String a) {
 340         int result = a.indexOf(emptyString);
 341         return a.substring(result, result + emptyString.length());
 342     }
 343 
 344     @Test(op = Operation.INDEX_OF_CON_UL, constString = stringSmallL)
 345     public static String indexOfConstUL(String a) {
 346         int result = a.indexOf(stringSmallL);
 347         return a.substring(result, result + stringSmallL.length());
 348     }
 349 
 350     @Test(op = Operation.INDEX_OF_CON_UL, constString = stringL)
 351     public static String indexOfConstLargeUL(String a) {
 352         int result = a.indexOf(stringL);
 353         return a.substring(result, result + stringL.length());
 354     }
 355 
 356     @Test(op = Operation.INDEX_OF_CHAR)
 357     public static int indexOfChar(String a, int ch, int from) {
 358         return a.indexOf(ch, from);
 359     }
 360 
 361     /*
 362      * String concatenation optimization
 363      */
 364     @Test(op = Operation.CONCAT, inStrings = {"ABC", "\uff21\uff22\uff23"}, outStrings = {"ABC", "\uff21\uff22\uff23"})
 365     public static String concatString(String a) {
 366         return new StringBuilder().append(a).toString();
 367     }
 368 
 369     @Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {""})
 370     public static String concatStringEmpty(String a) {
 371         return new StringBuilder().toString();
 372     }
 373 
 374     @Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {"null"})
 375     public static String concatStringNull(String a) {
 376         return new StringBuilder().append((String)null).toString();
 377     }
 378 
 379     @Test(op = Operation.CONCAT, inStrings = {"ABC", "\uff21\uff22\uff23"}, outStrings = {"abcdefghijklmnopABCabc", "abcdefghijklmnop\uff21\uff22\uff23abc"})
 380     public static String concatStringConstL(String a) {
 381         return new StringBuilder().append(stringL).append(a).append(stringSmallL).toString();
 382     }
 383 
 384     @Test(op = Operation.CONCAT, inStrings = {"ABC", "\uff21\uff22\uff23"}, outStrings = {"\u0f21\u0f22\u0f23ABC\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28", "\u0f21\u0f22\u0f23\uff21\uff22\uff23\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28"})
 385     public static String concatStringConstU(String a) {
 386         return new StringBuilder().append(stringSmallU).append(a).append(stringU).toString();
 387     }
 388 
 389     @Test(op = Operation.CONCAT_C, inChars = {'A', '\uff21'}, outStrings = {"A", "\uff21"})
 390     public static String concatChar(char a) {
 391         return new StringBuilder().append(a).toString();
 392     }
 393 
 394     @Test(op = Operation.CONCAT_C, inChars = {'A', '\uff21'}, outStrings = {"abcdefghijklmnopAabcA\uff21", "abcdefghijklmnop\uff21abcA\uff21"})
 395     public static String concatCharConstL(char a) {
 396         return new StringBuilder().append(stringL).append(a).append(stringSmallL).append(charL).append(charU).toString();
 397     }
 398 
 399     @Test(op = Operation.CONCAT_C, inChars = {'A', '\uff21'}, outStrings = {"\u0f21\u0f22\u0f23A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21A", "\u0f21\u0f22\u0f23\uff21\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21A"})
 400     public static String concatCharConstU(char a) {
 401         return new StringBuilder().append(stringSmallU).append(a).append(stringU).append(charU).append(charL).toString();
 402     }
 403 
 404     @Test(op = Operation.CONCAT_I, inInts = {Integer.MIN_VALUE, -42, 42, Integer.MAX_VALUE}, outStrings = {"-2147483648", "-42", "42", "2147483647"})
 405     public static String concatInt(int a) {
 406         return new StringBuilder().append(a).toString();
 407     }
 408 
 409     @Test(op = Operation.CONCAT_I, inInts = {Integer.MIN_VALUE, -42, 42, Integer.MAX_VALUE}, outStrings = {"abcdefghijklmnop-2147483648abc123-123", "abcdefghijklmnop-42abc123-123", "abcdefghijklmnop42abc123-123", "abcdefghijklmnop2147483647abc123-123"})
 410     public static String concatIntConstL(int b) {
 411         return new StringBuilder().append(stringL).append(b).append(stringSmallL).append(constInt).append(constIntNeg).toString();
 412     }
 413 
 414     @Test(op = Operation.CONCAT_I, inInts = {Integer.MIN_VALUE, -42, 42, Integer.MAX_VALUE}, outStrings = {"\u0f21\u0f22\u0f23-2147483648\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28123-123", "\u0f21\u0f22\u0f23-42\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28123-123", "\u0f21\u0f22\u0f2342\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28123-123", "\u0f21\u0f22\u0f232147483647\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28123-123"})
 415     public static String concatIntConstU(int b) {
 416         return new StringBuilder().append(stringSmallU).append(b).append(stringU).append(constInt).append(constIntNeg).toString();
 417     }
 418 
 419     @Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {"nullabcabcdefghijklmnopA123-123"})
 420     public static String concatConstL(String a) {
 421         return new StringBuilder().append((String)null).append(stringSmallL).append(stringL).append(charL).append(constInt).append(constIntNeg).toString();
 422     }
 423 
 424     @Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {"nullabcabcdefghijklmnop\u0f21\u0f22\u0f23\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28A\uff21123-123"})
 425     public static String concatConstU(String a) {
 426         return new StringBuilder().append((String)null).append(stringSmallL).append(stringL).append(stringSmallU).append(stringU).append(charL).append(charU).append(constInt).append(constIntNeg).toString();
 427     }
 428 
 429     @Test(op = Operation.CONCAT_M,
 430           inStrings = {"ABCDEFG", "ABCDEFG", "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28", "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28"},
 431           inChars = {'A', '\uff21', 'A', '\uff21'},
 432           inInts = {Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE},
 433           outStrings = {"ABCDEFGA-2147483648nullabcdefghijklmnop123-123A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21ABCDEFGA-2147483648null",
 434                         "ABCDEFG\uff212147483647nullabcdefghijklmnop123-123A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21ABCDEFG\uff212147483647null",
 435                         "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28A-2147483648nullabcdefghijklmnop123-123A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28A-2147483648null",
 436             "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff212147483647nullabcdefghijklmnop123-123A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff212147483647null"})
 437     public static String concatMixed(String a, char b, int c) {
 438         return new StringBuilder().append(a).append(b).append(c).append((String)null)
 439                 .append(stringL).append(constInt).append(constIntNeg).append(charL).append(stringU).append(charU)
 440                 .append(a).append(b).append(c).append((String)null).toString();
 441     }
 442 }