blob: 36cf6827b9b16ef11de4cbde0b9e47d946c027d6 [file] [log] [blame]
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8054307
* @summary Tests correctness of string related intrinsics and C2 optimizations.
*
* @run main/timeout=240 compiler.intrinsics.string.TestStringIntrinsics
*/
package compiler.intrinsics.string;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Arrays;
public class TestStringIntrinsics {
public enum Operation {
ARR_EQUALS_B, ARR_EQUALS_C, EQUALS, COMPARE_TO, INDEX_OF, INDEX_OF_CON_U, INDEX_OF_CON_L,
INDEX_OF_CON_UL, CONCAT, CONCAT_C, CONCAT_I, CONCAT_M, INDEX_OF_CHAR
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Test {
Operation op();
String constString() default "";
String[] inStrings() default {};
char[] inChars() default {};
int[] inInts() default {};
String[] outStrings() default {};
}
public static void main(String[] args) throws Exception {
new TestStringIntrinsics().run();
}
public void run() throws Exception {
// Build latin1 and UTF16 strings
StringBuilder latin1Builder = new StringBuilder();
for (int i = 0; i <= 255; ++i) {
latin1Builder.append((char) i);
}
String latin1 = latin1Builder.toString();
StringBuilder utf16Builder = new StringBuilder();
for (int i = 0; i <= 10000; ++i) {
utf16Builder.append((char) i);
}
String utf16 = utf16Builder.toString();
// Invoke test methods
for (Method m : TestStringIntrinsics.class.getMethods()) {
if (m.isAnnotationPresent(Test.class)) {
System.out.print("Checking " + m.getName() + "... ");
Operation op = m.getAnnotation(Test.class).op();
Test antn = m.getAnnotation(Test.class);
if (isStringConcatTest(op)) {
checkStringConcat(op, m, antn);
} else {
checkIntrinsics(op, m, latin1, utf16, antn);
}
System.out.println("Done.");
}
}
}
private boolean isStringConcatTest(Operation op) {
return op == Operation.CONCAT ||
op == Operation.CONCAT_C ||
op == Operation.CONCAT_I ||
op == Operation.CONCAT_M;
}
/**
* Checks correctness of the String.equals, String.compareTo and String.indexOf intrinsics.
* -XX:SpecialStringEquals
* -XX:SpecialStringCompareTo
* -XX:SpecialStringIndexOf
*/
private void checkIntrinsics(Operation op, Method m, String latin1, String utf16, Test antn) throws Exception {
for (int i = 0; i < 50_000; ++i) {
// Copy and permute latin1 and UTF16 string
char[] arrL = latin1.toCharArray();
int indexL = i % arrL.length;
int mod = (arrL.length - arrL[indexL]);
int incL = i % ((mod != 0) ? mod : 1);
arrL[indexL] = (char) ((int) arrL[indexL] + incL);
String latin1Copy = String.valueOf(arrL);
char[] arrU = utf16.toCharArray();
int indexU = i % arrU.length;
mod = (arrU.length - arrU[indexU]);
int incU = i % ((mod != 0) ? mod : 1);
arrU[indexU] = (char) ((int) arrU[indexU] + incU);
String utf16Copy = String.valueOf(arrU);
switch (op) {
case ARR_EQUALS_B:
invokeAndCheck(m, (incL == 0), latin1.getBytes("ISO-8859-1"), latin1Copy.getBytes("ISO-8859-1"));
invokeAndCheck(m, true, new byte[] {1, 2, 3}, new byte[] {1, 2, 3});
invokeAndCheck(m, true, new byte[] {1}, new byte[] {1});
invokeAndCheck(m, true, new byte[] {}, new byte[] {});
break;
case ARR_EQUALS_C:
invokeAndCheck(m, (incU == 0), utf16.toCharArray(), arrU);
break;
case EQUALS:
invokeAndCheck(m, (incL == 0), latin1, latin1Copy);
invokeAndCheck(m, false, latin1, "");
invokeAndCheck(m, false, "", latin1);
invokeAndCheck(m, (incU == 0), utf16, utf16Copy);
invokeAndCheck(m, false, utf16, "");
invokeAndCheck(m, false, "", utf16);
invokeAndCheck(m, false, latin1, utf16);
break;
case COMPARE_TO:
invokeAndCheck(m, -incL, latin1, latin1Copy);
invokeAndCheck(m, latin1.length(), latin1, "");
invokeAndCheck(m, -incU, utf16, utf16Copy);
invokeAndCheck(m, utf16.length(), utf16, "");
// Cross coder
char cL = latin1.charAt(indexL);
char cU = utf16.charAt(indexU);
invokeAndCheck(m, cL - cU, latin1, latin1.replace(cL, cU));
invokeAndCheck(m, cU - cL, utf16, utf16.replace(cU, cL));
// Different lengths
invokeAndCheck(m, 1, "ABCD", "ABC");
invokeAndCheck(m, -1, "\uff21\uff22\uff23", "\uff21\uff22\uff23\uff24");
invokeAndCheck(m, 1, "ABC\uff24", "ABC");
invokeAndCheck(m, 3, "ABC\uff24\uff25\uff26", "ABC");
invokeAndCheck(m, -1, "ABC","ABC\uff24");
invokeAndCheck(m, -3, "ABC","ABC\uff24\uff25\uff26");
break;
case INDEX_OF:
invokeAndCheck(m, indexL, latin1, latin1.substring(indexL), (indexL > 42) ? 42 : 0);
invokeAndCheck(m, 0, latin1, "", 0);
invokeAndCheck(m, indexU, utf16, utf16.substring(indexU), (indexU > 42) ? 42 : 0);
invokeAndCheck(m, 0, utf16, "", 0);
// Cross coder
invokeAndCheck(m, -1, latin1.substring(0, indexL), utf16.substring(indexU), (indexL > 42) ? 42 : 0);
// Skip latin1 chars in utf16 string
int start = 256;
int end = indexU > start ? indexU : start;
invokeAndCheck(m, end-start, utf16.substring(start, end) + latin1.substring(indexL), latin1.substring(indexL), 0);
break;
case INDEX_OF_CON_L:
invokeAndCheck(m, antn.constString(), latin1);
break;
case INDEX_OF_CON_U:
invokeAndCheck(m, antn.constString(), utf16);
break;
case INDEX_OF_CON_UL:
invokeAndCheck(m, antn.constString(), utf16);
break;
case INDEX_OF_CHAR:
invokeAndCheck(m, 7, "abcdefg\uD800\uDC00", 65536, 0);
invokeAndCheck(m, -1, "abcdefg\uD800\uDC01", 65536, 0);
invokeAndCheck(m, -1, "abcdefg\uD800", 65536, 0);
invokeAndCheck(m, 3, "abc\u0107", 263, 0);
invokeAndCheck(m, -1, "abc\u0108", 263, 0);
invokeAndCheck(m, 7, "abcdefg\u0107", 263, 0);
invokeAndCheck(m, 7, "abcdefg\u0107", 263, -1);
invokeAndCheck(m, 0, "\u0107", 263, 0);
break;
default:
throw new RuntimeException("Unexpected operation.");
}
}
}
/**
* Checks correctness of the C2 string concatenation optimization.
* -XX:OptimizeStringConcat
*/
private void checkStringConcat(Operation op, Method m, Test antn) throws Exception {
for (int i = 0; i < 50_000; ++i) {
String[] result = antn.outStrings();
switch(op) {
case CONCAT:
String[] strs = antn.inStrings();
for (int j = 0; j < strs.length; ++j) {
invokeAndCheck(m, result[j], strs[j]);
}
break;
case CONCAT_C:
char[] ch = antn.inChars();
for (int j = 0; j < ch.length; ++j) {
invokeAndCheck(m, result[j], ch[j]);
}
break;
case CONCAT_I:
int[] k = antn.inInts();
for (int j = 0; j < k.length; ++j) {
invokeAndCheck(m, result[j], k[j]);
}
break;
case CONCAT_M:
strs = antn.inStrings();
ch = antn.inChars();
k = antn.inInts();
for (int j = 0; j < strs.length; ++j) {
invokeAndCheck(m, result[j], strs[j], ch[j], k[j]);
}
break;
default:
throw new RuntimeException("Unexpected operation.");
}
}
}
/**
* Invokes method 'm' by passing arguments 'args' and checks if the
* returned value equals 'expectedResult'.
*/
private void invokeAndCheck(Method m, Object expectedResult, Object... args) throws Exception {
Object result = m.invoke(null, args);
if (!result.equals(expectedResult)) {
// System.out.println("Expected:");
// System.out.println(expectedResult);
// System.out.println("Returned:");
// System.out.println(result);
throw new RuntimeException("Result of '" + m.getName() + "' not equal to expected value.");
}
}
/*
* Constants
*/
static final char charU = '\uff21';
static final char charL = 'A';
static final String emptyString = "";
static final String stringL = "abcdefghijklmnop";
static final String stringSmallL = "abc";
static final String stringU = "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28";
static final String stringSmallU = "\u0f21\u0f22\u0f23";
static final int constInt = 123;
static final int constIntNeg = -123;
/*
* Arrays.equals
*/
@Test(op = Operation.ARR_EQUALS_B)
public static boolean arrayEqualsB(byte[] a, byte[] b) {
return Arrays.equals(a, b);
}
@Test(op = Operation.ARR_EQUALS_C)
public static boolean arrayEqualsC(char[] a, char[] b) {
return Arrays.equals(a, b);
}
/*
* String.equals
*/
@Test(op = Operation.EQUALS)
public static boolean equals(String a, String b) {
return a.equals(b);
}
/*
* String.compareTo
*/
@Test(op = Operation.COMPARE_TO)
public static int compareTo(String a, String b) {
return a.compareTo(b);
}
/*
* String.indexOf
*/
@Test(op = Operation.INDEX_OF)
public static int indexOf(String a, String b, int from) {
return a.indexOf(b, from);
}
@Test(op = Operation.INDEX_OF_CON_U, constString = stringSmallU)
public static String indexOfConstU(String a) {
int result = a.indexOf(stringSmallU);
return a.substring(result, result + stringSmallU.length());
}
@Test(op = Operation.INDEX_OF_CON_U, constString = stringU)
public static String indexOfConstLargeU(String a) {
int result = a.indexOf(stringU);
return a.substring(result, result + stringU.length());
}
@Test(op = Operation.INDEX_OF_CON_U, constString = emptyString)
public static String indexOfConstEmptyU(String a) {
int result = a.indexOf(emptyString);
return a.substring(result, result + emptyString.length());
}
@Test(op = Operation.INDEX_OF_CON_L, constString = stringSmallL)
public static String indexOfConstL(String a) {
int result = a.indexOf(stringSmallL);
return a.substring(result, result + stringSmallL.length());
}
@Test(op = Operation.INDEX_OF_CON_L, constString = stringL)
public static String indexOfConstLargeL(String a) {
int result = a.indexOf(stringL);
return a.substring(result, result + stringL.length());
}
@Test(op = Operation.INDEX_OF_CON_L, constString = emptyString)
public static String indexOfConstEmptyL(String a) {
int result = a.indexOf(emptyString);
return a.substring(result, result + emptyString.length());
}
@Test(op = Operation.INDEX_OF_CON_UL, constString = stringSmallL)
public static String indexOfConstUL(String a) {
int result = a.indexOf(stringSmallL);
return a.substring(result, result + stringSmallL.length());
}
@Test(op = Operation.INDEX_OF_CON_UL, constString = stringL)
public static String indexOfConstLargeUL(String a) {
int result = a.indexOf(stringL);
return a.substring(result, result + stringL.length());
}
@Test(op = Operation.INDEX_OF_CHAR)
public static int indexOfChar(String a, int ch, int from) {
return a.indexOf(ch, from);
}
/*
* String concatenation optimization
*/
@Test(op = Operation.CONCAT, inStrings = {"ABC", "\uff21\uff22\uff23"}, outStrings = {"ABC", "\uff21\uff22\uff23"})
public static String concatString(String a) {
return new StringBuilder().append(a).toString();
}
@Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {""})
public static String concatStringEmpty(String a) {
return new StringBuilder().toString();
}
@Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {"null"})
public static String concatStringNull(String a) {
return new StringBuilder().append((String)null).toString();
}
@Test(op = Operation.CONCAT, inStrings = {"ABC", "\uff21\uff22\uff23"}, outStrings = {"abcdefghijklmnopABCabc", "abcdefghijklmnop\uff21\uff22\uff23abc"})
public static String concatStringConstL(String a) {
return new StringBuilder().append(stringL).append(a).append(stringSmallL).toString();
}
@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"})
public static String concatStringConstU(String a) {
return new StringBuilder().append(stringSmallU).append(a).append(stringU).toString();
}
@Test(op = Operation.CONCAT_C, inChars = {'A', '\uff21'}, outStrings = {"A", "\uff21"})
public static String concatChar(char a) {
return new StringBuilder().append(a).toString();
}
@Test(op = Operation.CONCAT_C, inChars = {'A', '\uff21'}, outStrings = {"abcdefghijklmnopAabcA\uff21", "abcdefghijklmnop\uff21abcA\uff21"})
public static String concatCharConstL(char a) {
return new StringBuilder().append(stringL).append(a).append(stringSmallL).append(charL).append(charU).toString();
}
@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"})
public static String concatCharConstU(char a) {
return new StringBuilder().append(stringSmallU).append(a).append(stringU).append(charU).append(charL).toString();
}
@Test(op = Operation.CONCAT_I, inInts = {Integer.MIN_VALUE, -42, 42, Integer.MAX_VALUE}, outStrings = {"-2147483648", "-42", "42", "2147483647"})
public static String concatInt(int a) {
return new StringBuilder().append(a).toString();
}
@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"})
public static String concatIntConstL(int b) {
return new StringBuilder().append(stringL).append(b).append(stringSmallL).append(constInt).append(constIntNeg).toString();
}
@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"})
public static String concatIntConstU(int b) {
return new StringBuilder().append(stringSmallU).append(b).append(stringU).append(constInt).append(constIntNeg).toString();
}
@Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {"nullabcabcdefghijklmnopA123-123"})
public static String concatConstL(String a) {
return new StringBuilder().append((String)null).append(stringSmallL).append(stringL).append(charL).append(constInt).append(constIntNeg).toString();
}
@Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {"nullabcabcdefghijklmnop\u0f21\u0f22\u0f23\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28A\uff21123-123"})
public static String concatConstU(String a) {
return new StringBuilder().append((String)null).append(stringSmallL).append(stringL).append(stringSmallU).append(stringU).append(charL).append(charU).append(constInt).append(constIntNeg).toString();
}
@Test(op = Operation.CONCAT_M,
inStrings = {"ABCDEFG", "ABCDEFG", "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28", "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28"},
inChars = {'A', '\uff21', 'A', '\uff21'},
inInts = {Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE},
outStrings = {"ABCDEFGA-2147483648nullabcdefghijklmnop123-123A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21ABCDEFGA-2147483648null",
"ABCDEFG\uff212147483647nullabcdefghijklmnop123-123A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21ABCDEFG\uff212147483647null",
"\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",
"\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"})
public static String concatMixed(String a, char b, int c) {
return new StringBuilder().append(a).append(b).append(c).append((String)null)
.append(stringL).append(constInt).append(constIntNeg).append(charL).append(stringU).append(charU)
.append(a).append(b).append(c).append((String)null).toString();
}
}