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
32package compiler.intrinsics.string;
33
34import java.lang.annotation.ElementType;
35import java.lang.annotation.Retention;
36import java.lang.annotation.RetentionPolicy;
37import java.lang.annotation.Target;
38import java.lang.reflect.Method;
39import java.util.Arrays;
40
41public 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}
443