1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 public class Main {
main(String[] args)18     public static void main(String[] args) {
19         testAppendStringAndLong();
20         testAppendStringAndInt();
21         testAppendStringAndString();
22         testMiscelaneous();
23         testNoArgs();
24         testInline();
25         testEquals();
26         System.out.println("passed");
27     }
28 
29     private static final String APPEND_LONG_PREFIX = "Long/";
30     private static final String[] APPEND_LONG_TEST_CASES = {
31         "Long/0",
32         "Long/1",
33         "Long/9",
34         "Long/10",
35         "Long/99",
36         "Long/100",
37         "Long/999",
38         "Long/1000",
39         "Long/9999",
40         "Long/10000",
41         "Long/99999",
42         "Long/100000",
43         "Long/999999",
44         "Long/1000000",
45         "Long/9999999",
46         "Long/10000000",
47         "Long/99999999",
48         "Long/100000000",
49         "Long/999999999",
50         "Long/1000000000",
51         "Long/9999999999",
52         "Long/10000000000",
53         "Long/99999999999",
54         "Long/100000000000",
55         "Long/999999999999",
56         "Long/1000000000000",
57         "Long/9999999999999",
58         "Long/10000000000000",
59         "Long/99999999999999",
60         "Long/100000000000000",
61         "Long/999999999999999",
62         "Long/1000000000000000",
63         "Long/9999999999999999",
64         "Long/10000000000000000",
65         "Long/99999999999999999",
66         "Long/100000000000000000",
67         "Long/999999999999999999",
68         "Long/1000000000000000000",
69         "Long/9223372036854775807",  // Long.MAX_VALUE
70         "Long/-1",
71         "Long/-9",
72         "Long/-10",
73         "Long/-99",
74         "Long/-100",
75         "Long/-999",
76         "Long/-1000",
77         "Long/-9999",
78         "Long/-10000",
79         "Long/-99999",
80         "Long/-100000",
81         "Long/-999999",
82         "Long/-1000000",
83         "Long/-9999999",
84         "Long/-10000000",
85         "Long/-99999999",
86         "Long/-100000000",
87         "Long/-999999999",
88         "Long/-1000000000",
89         "Long/-9999999999",
90         "Long/-10000000000",
91         "Long/-99999999999",
92         "Long/-100000000000",
93         "Long/-999999999999",
94         "Long/-1000000000000",
95         "Long/-9999999999999",
96         "Long/-10000000000000",
97         "Long/-99999999999999",
98         "Long/-100000000000000",
99         "Long/-999999999999999",
100         "Long/-1000000000000000",
101         "Long/-9999999999999999",
102         "Long/-10000000000000000",
103         "Long/-99999999999999999",
104         "Long/-100000000000000000",
105         "Long/-999999999999999999",
106         "Long/-1000000000000000000",
107         "Long/-9223372036854775808",  // Long.MIN_VALUE
108     };
109 
110     /// CHECK-START: java.lang.String Main.$noinline$appendStringAndLong(java.lang.String, long) instruction_simplifier (before)
111     /// CHECK-NOT:              StringBuilderAppend
112 
113     /// CHECK-START: java.lang.String Main.$noinline$appendStringAndLong(java.lang.String, long) instruction_simplifier (after)
114     /// CHECK:                  StringBuilderAppend
$noinline$appendStringAndLong(String s, long l)115     public static String $noinline$appendStringAndLong(String s, long l) {
116         return new StringBuilder().append(s).append(l).toString();
117     }
118 
testAppendStringAndLong()119     public static void testAppendStringAndLong() {
120         for (String expected : APPEND_LONG_TEST_CASES) {
121             long l = Long.valueOf(expected.substring(APPEND_LONG_PREFIX.length()));
122             String result = $noinline$appendStringAndLong(APPEND_LONG_PREFIX, l);
123             assertEquals(expected, result);
124         }
125     }
126 
127     private static final String APPEND_INT_PREFIX = "Int/";
128     private static final String[] APPEND_INT_TEST_CASES = {
129         "Int/0",
130         "Int/1",
131         "Int/9",
132         "Int/10",
133         "Int/99",
134         "Int/100",
135         "Int/999",
136         "Int/1000",
137         "Int/9999",
138         "Int/10000",
139         "Int/99999",
140         "Int/100000",
141         "Int/999999",
142         "Int/1000000",
143         "Int/9999999",
144         "Int/10000000",
145         "Int/99999999",
146         "Int/100000000",
147         "Int/999999999",
148         "Int/1000000000",
149         "Int/2147483647",  // Integer.MAX_VALUE
150         "Int/-1",
151         "Int/-9",
152         "Int/-10",
153         "Int/-99",
154         "Int/-100",
155         "Int/-999",
156         "Int/-1000",
157         "Int/-9999",
158         "Int/-10000",
159         "Int/-99999",
160         "Int/-100000",
161         "Int/-999999",
162         "Int/-1000000",
163         "Int/-9999999",
164         "Int/-10000000",
165         "Int/-99999999",
166         "Int/-100000000",
167         "Int/-999999999",
168         "Int/-1000000000",
169         "Int/-2147483648",  // Integer.MIN_VALUE
170     };
171 
172     /// CHECK-START: java.lang.String Main.$noinline$appendStringAndInt(java.lang.String, int) instruction_simplifier (before)
173     /// CHECK-NOT:              StringBuilderAppend
174 
175     /// CHECK-START: java.lang.String Main.$noinline$appendStringAndInt(java.lang.String, int) instruction_simplifier (after)
176     /// CHECK:                  StringBuilderAppend
$noinline$appendStringAndInt(String s, int i)177     public static String $noinline$appendStringAndInt(String s, int i) {
178         return new StringBuilder().append(s).append(i).toString();
179     }
180 
testAppendStringAndInt()181     public static void testAppendStringAndInt() {
182         for (String expected : APPEND_INT_TEST_CASES) {
183             int i = Integer.valueOf(expected.substring(APPEND_INT_PREFIX.length()));
184             String result = $noinline$appendStringAndInt(APPEND_INT_PREFIX, i);
185             assertEquals(expected, result);
186         }
187     }
188 
$noinline$appendStringAndString(String s1, String s2)189     public static String $noinline$appendStringAndString(String s1, String s2) {
190         return new StringBuilder().append(s1).append(s2).toString();
191     }
192 
testAppendStringAndString()193     public static void testAppendStringAndString() {
194         assertEquals("nullnull", $noinline$appendStringAndString(null, null));
195         assertEquals("nullTEST", $noinline$appendStringAndString(null, "TEST"));
196         assertEquals("TESTnull", $noinline$appendStringAndString("TEST", null));
197         assertEquals("abcDEFGH", $noinline$appendStringAndString("abc", "DEFGH"));
198         // Test with a non-ASCII character.
199         assertEquals("test\u0131", $noinline$appendStringAndString("test", "\u0131"));
200         assertEquals("\u0131test", $noinline$appendStringAndString("\u0131", "test"));
201         assertEquals("\u0131test\u0131", $noinline$appendStringAndString("\u0131", "test\u0131"));
202     }
203 
204     /// CHECK-START: java.lang.String Main.$noinline$appendSLILC(java.lang.String, long, int, long, char) instruction_simplifier (before)
205     /// CHECK-NOT:              StringBuilderAppend
206 
207     /// CHECK-START: java.lang.String Main.$noinline$appendSLILC(java.lang.String, long, int, long, char) instruction_simplifier (after)
208     /// CHECK:                  StringBuilderAppend
$noinline$appendSLILC(String s, long l1, int i, long l2, char c)209     public static String $noinline$appendSLILC(String s,
210                                                long l1,
211                                                int i,
212                                                long l2,
213                                                char c) {
214         return new StringBuilder().append(s)
215                                   .append(l1)
216                                   .append(i)
217                                   .append(l2)
218                                   .append(c).toString();
219     }
220 
testMiscelaneous()221     public static void testMiscelaneous() {
222         assertEquals("x17-1q",
223                      $noinline$appendSLILC("x", 1L, 7, -1L, 'q'));
224         assertEquals("null17-1q",
225                      $noinline$appendSLILC(null, 1L, 7, -1L, 'q'));
226         assertEquals("x\u013117-1q",
227                      $noinline$appendSLILC("x\u0131", 1L, 7, -1L, 'q'));
228         assertEquals("x427-1q",
229                      $noinline$appendSLILC("x", 42L, 7, -1L, 'q'));
230         assertEquals("x1-42-1q",
231                      $noinline$appendSLILC("x", 1L, -42, -1L, 'q'));
232         assertEquals("x17424242q",
233                      $noinline$appendSLILC("x", 1L, 7, 424242L, 'q'));
234         assertEquals("x17-1\u0131",
235                      $noinline$appendSLILC("x", 1L, 7, -1L, '\u0131'));
236     }
237 
$inline$testInlineInner(StringBuilder sb, String s, int i)238     public static String $inline$testInlineInner(StringBuilder sb, String s, int i) {
239         return sb.append(s).append(i).toString();
240     }
241 
242     /// CHECK-START: java.lang.String Main.$noinline$testInlineOuter(java.lang.String, int) instruction_simplifier$after_inlining (before)
243     /// CHECK-NOT:              StringBuilderAppend
244 
245     /// CHECK-START: java.lang.String Main.$noinline$testInlineOuter(java.lang.String, int) instruction_simplifier$after_inlining (after)
246     /// CHECK:                  StringBuilderAppend
$noinline$testInlineOuter(String s, int i)247     public static String $noinline$testInlineOuter(String s, int i) {
248         StringBuilder sb = new StringBuilder();
249         return $inline$testInlineInner(sb, s, i);
250     }
251 
testInline()252     public static void testInline() {
253         assertEquals("x42", $noinline$testInlineOuter("x", 42));
254     }
255 
256     /// CHECK-START: java.lang.String Main.$noinline$appendNothing() instruction_simplifier (before)
257     /// CHECK-NOT:              StringBuilderAppend
258 
259     /// CHECK-START: java.lang.String Main.$noinline$appendNothing() instruction_simplifier (after)
260     /// CHECK-NOT:              StringBuilderAppend
$noinline$appendNothing()261     public static String $noinline$appendNothing() {
262         return new StringBuilder().toString();
263     }
264 
testNoArgs()265     public static void testNoArgs() {
266         assertEquals("", $noinline$appendNothing());
267     }
268 
269     /// CHECK-START: boolean Main.$noinline$testAppendEquals(java.lang.String, int) instruction_simplifier (before)
270     /// CHECK-NOT:              StringBuilderAppend
271 
272     /// CHECK-START: boolean Main.$noinline$testAppendEquals(java.lang.String, int) instruction_simplifier (after)
273     /// CHECK:                  StringBuilderAppend
$noinline$testAppendEquals(String s, int i)274     public static boolean $noinline$testAppendEquals(String s, int i) {
275       // Regression test for b/151107293 .
276       // When a string is used as both receiver and argument of String.equals(), we DCHECK()
277       // that it cannot be null. However, when replacing the call to StringBuilder.toString()
278       // with the HStringBuilderAppend(), the former reported CanBeNull() as false and
279       // therefore no explicit null checks were needed, but the replacement reported
280       // CanBeNull() as true, so when the result was used in String.equals() for both
281       // receiver and argument, the DCHECK() failed. This was fixed by overriding
282       // CanBeNull() in HStringBuilderAppend to correctly return false; the string that
283       // previously didn't require null check still does not require it.
284       String str = new StringBuilder().append(s).append(i).toString();
285       return str.equals(str);
286     }
287 
testEquals()288     public static void testEquals() {
289       if (!$noinline$testAppendEquals("Test", 42)) {
290         throw new Error("str.equals(str) is false");
291       }
292     }
293 
assertEquals(String expected, String actual)294     public static void assertEquals(String expected, String actual) {
295         if (!expected.equals(actual)) {
296             throw new AssertionError("Expected: " + expected + ", actual: " + actual);
297         }
298     }
299 }
300