/* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ public class Main { public static void main(String[] args) { testAppendStringAndLong(); testAppendStringAndInt(); testAppendStringAndString(); testMiscelaneous(); testNoArgs(); testInline(); testEquals(); System.out.println("passed"); } private static final String APPEND_LONG_PREFIX = "Long/"; private static final String[] APPEND_LONG_TEST_CASES = { "Long/0", "Long/1", "Long/9", "Long/10", "Long/99", "Long/100", "Long/999", "Long/1000", "Long/9999", "Long/10000", "Long/99999", "Long/100000", "Long/999999", "Long/1000000", "Long/9999999", "Long/10000000", "Long/99999999", "Long/100000000", "Long/999999999", "Long/1000000000", "Long/9999999999", "Long/10000000000", "Long/99999999999", "Long/100000000000", "Long/999999999999", "Long/1000000000000", "Long/9999999999999", "Long/10000000000000", "Long/99999999999999", "Long/100000000000000", "Long/999999999999999", "Long/1000000000000000", "Long/9999999999999999", "Long/10000000000000000", "Long/99999999999999999", "Long/100000000000000000", "Long/999999999999999999", "Long/1000000000000000000", "Long/9223372036854775807", // Long.MAX_VALUE "Long/-1", "Long/-9", "Long/-10", "Long/-99", "Long/-100", "Long/-999", "Long/-1000", "Long/-9999", "Long/-10000", "Long/-99999", "Long/-100000", "Long/-999999", "Long/-1000000", "Long/-9999999", "Long/-10000000", "Long/-99999999", "Long/-100000000", "Long/-999999999", "Long/-1000000000", "Long/-9999999999", "Long/-10000000000", "Long/-99999999999", "Long/-100000000000", "Long/-999999999999", "Long/-1000000000000", "Long/-9999999999999", "Long/-10000000000000", "Long/-99999999999999", "Long/-100000000000000", "Long/-999999999999999", "Long/-1000000000000000", "Long/-9999999999999999", "Long/-10000000000000000", "Long/-99999999999999999", "Long/-100000000000000000", "Long/-999999999999999999", "Long/-1000000000000000000", "Long/-9223372036854775808", // Long.MIN_VALUE }; /// CHECK-START: java.lang.String Main.$noinline$appendStringAndLong(java.lang.String, long) instruction_simplifier (before) /// CHECK-NOT: StringBuilderAppend /// CHECK-START: java.lang.String Main.$noinline$appendStringAndLong(java.lang.String, long) instruction_simplifier (after) /// CHECK: StringBuilderAppend public static String $noinline$appendStringAndLong(String s, long l) { return new StringBuilder().append(s).append(l).toString(); } public static void testAppendStringAndLong() { for (String expected : APPEND_LONG_TEST_CASES) { long l = Long.valueOf(expected.substring(APPEND_LONG_PREFIX.length())); String result = $noinline$appendStringAndLong(APPEND_LONG_PREFIX, l); assertEquals(expected, result); } } private static final String APPEND_INT_PREFIX = "Int/"; private static final String[] APPEND_INT_TEST_CASES = { "Int/0", "Int/1", "Int/9", "Int/10", "Int/99", "Int/100", "Int/999", "Int/1000", "Int/9999", "Int/10000", "Int/99999", "Int/100000", "Int/999999", "Int/1000000", "Int/9999999", "Int/10000000", "Int/99999999", "Int/100000000", "Int/999999999", "Int/1000000000", "Int/2147483647", // Integer.MAX_VALUE "Int/-1", "Int/-9", "Int/-10", "Int/-99", "Int/-100", "Int/-999", "Int/-1000", "Int/-9999", "Int/-10000", "Int/-99999", "Int/-100000", "Int/-999999", "Int/-1000000", "Int/-9999999", "Int/-10000000", "Int/-99999999", "Int/-100000000", "Int/-999999999", "Int/-1000000000", "Int/-2147483648", // Integer.MIN_VALUE }; /// CHECK-START: java.lang.String Main.$noinline$appendStringAndInt(java.lang.String, int) instruction_simplifier (before) /// CHECK-NOT: StringBuilderAppend /// CHECK-START: java.lang.String Main.$noinline$appendStringAndInt(java.lang.String, int) instruction_simplifier (after) /// CHECK: StringBuilderAppend public static String $noinline$appendStringAndInt(String s, int i) { return new StringBuilder().append(s).append(i).toString(); } public static void testAppendStringAndInt() { for (String expected : APPEND_INT_TEST_CASES) { int i = Integer.valueOf(expected.substring(APPEND_INT_PREFIX.length())); String result = $noinline$appendStringAndInt(APPEND_INT_PREFIX, i); assertEquals(expected, result); } } public static String $noinline$appendStringAndString(String s1, String s2) { return new StringBuilder().append(s1).append(s2).toString(); } public static void testAppendStringAndString() { assertEquals("nullnull", $noinline$appendStringAndString(null, null)); assertEquals("nullTEST", $noinline$appendStringAndString(null, "TEST")); assertEquals("TESTnull", $noinline$appendStringAndString("TEST", null)); assertEquals("abcDEFGH", $noinline$appendStringAndString("abc", "DEFGH")); // Test with a non-ASCII character. assertEquals("test\u0131", $noinline$appendStringAndString("test", "\u0131")); assertEquals("\u0131test", $noinline$appendStringAndString("\u0131", "test")); assertEquals("\u0131test\u0131", $noinline$appendStringAndString("\u0131", "test\u0131")); } /// CHECK-START: java.lang.String Main.$noinline$appendSLILC(java.lang.String, long, int, long, char) instruction_simplifier (before) /// CHECK-NOT: StringBuilderAppend /// CHECK-START: java.lang.String Main.$noinline$appendSLILC(java.lang.String, long, int, long, char) instruction_simplifier (after) /// CHECK: StringBuilderAppend public static String $noinline$appendSLILC(String s, long l1, int i, long l2, char c) { return new StringBuilder().append(s) .append(l1) .append(i) .append(l2) .append(c).toString(); } public static void testMiscelaneous() { assertEquals("x17-1q", $noinline$appendSLILC("x", 1L, 7, -1L, 'q')); assertEquals("null17-1q", $noinline$appendSLILC(null, 1L, 7, -1L, 'q')); assertEquals("x\u013117-1q", $noinline$appendSLILC("x\u0131", 1L, 7, -1L, 'q')); assertEquals("x427-1q", $noinline$appendSLILC("x", 42L, 7, -1L, 'q')); assertEquals("x1-42-1q", $noinline$appendSLILC("x", 1L, -42, -1L, 'q')); assertEquals("x17424242q", $noinline$appendSLILC("x", 1L, 7, 424242L, 'q')); assertEquals("x17-1\u0131", $noinline$appendSLILC("x", 1L, 7, -1L, '\u0131')); } public static String $inline$testInlineInner(StringBuilder sb, String s, int i) { return sb.append(s).append(i).toString(); } /// CHECK-START: java.lang.String Main.$noinline$testInlineOuter(java.lang.String, int) instruction_simplifier$after_inlining (before) /// CHECK-NOT: StringBuilderAppend /// CHECK-START: java.lang.String Main.$noinline$testInlineOuter(java.lang.String, int) instruction_simplifier$after_inlining (after) /// CHECK: StringBuilderAppend public static String $noinline$testInlineOuter(String s, int i) { StringBuilder sb = new StringBuilder(); return $inline$testInlineInner(sb, s, i); } public static void testInline() { assertEquals("x42", $noinline$testInlineOuter("x", 42)); } /// CHECK-START: java.lang.String Main.$noinline$appendNothing() instruction_simplifier (before) /// CHECK-NOT: StringBuilderAppend /// CHECK-START: java.lang.String Main.$noinline$appendNothing() instruction_simplifier (after) /// CHECK-NOT: StringBuilderAppend public static String $noinline$appendNothing() { return new StringBuilder().toString(); } public static void testNoArgs() { assertEquals("", $noinline$appendNothing()); } /// CHECK-START: boolean Main.$noinline$testAppendEquals(java.lang.String, int) instruction_simplifier (before) /// CHECK-NOT: StringBuilderAppend /// CHECK-START: boolean Main.$noinline$testAppendEquals(java.lang.String, int) instruction_simplifier (after) /// CHECK: StringBuilderAppend public static boolean $noinline$testAppendEquals(String s, int i) { // Regression test for b/151107293 . // When a string is used as both receiver and argument of String.equals(), we DCHECK() // that it cannot be null. However, when replacing the call to StringBuilder.toString() // with the HStringBuilderAppend(), the former reported CanBeNull() as false and // therefore no explicit null checks were needed, but the replacement reported // CanBeNull() as true, so when the result was used in String.equals() for both // receiver and argument, the DCHECK() failed. This was fixed by overriding // CanBeNull() in HStringBuilderAppend to correctly return false; the string that // previously didn't require null check still does not require it. String str = new StringBuilder().append(s).append(i).toString(); return str.equals(str); } public static void testEquals() { if (!$noinline$testAppendEquals("Test", 42)) { throw new Error("str.equals(str) is false"); } } public static void assertEquals(String expected, String actual) { if (!expected.equals(actual)) { throw new AssertionError("Expected: " + expected + ", actual: " + actual); } } }