1 /*
2  * Copyright (c) 2018, 2019, 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 package org.graalvm.compiler.replacements.test;
26 
27 import static org.junit.Assume.assumeFalse;
28 import static org.junit.Assume.assumeTrue;
29 
30 import java.io.UnsupportedEncodingException;
31 
32 import org.graalvm.compiler.core.common.CompilationIdentifier;
33 import org.graalvm.compiler.nodes.StructuredGraph;
34 import org.graalvm.compiler.replacements.amd64.AMD64StringLatin1InflateNode;
35 import org.graalvm.compiler.replacements.amd64.AMD64StringLatin1Substitutions;
36 import org.graalvm.compiler.replacements.amd64.AMD64StringUTF16CompressNode;
37 import org.graalvm.compiler.replacements.amd64.AMD64StringUTF16Substitutions;
38 import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
39 import org.graalvm.compiler.test.AddExports;
40 import org.junit.Before;
41 import org.junit.Test;
42 
43 import jdk.vm.ci.amd64.AMD64;
44 import jdk.vm.ci.code.InstalledCode;
45 import jdk.vm.ci.meta.ResolvedJavaMethod;
46 
47 /**
48  * Test intrinsic/node substitutions for (innate) methods StringLatin1.inflate and
49  * StringUTF16.compress provided by {@link AMD64StringLatin1Substitutions} and
50  * {@link AMD64StringUTF16Substitutions}.
51  */
52 @AddExports({"java.base/java.lang"})
53 public final class StringCompressInflateTest extends MethodSubstitutionTest {
54 
55     static final int N = 1000;
56 
57     @Before
checkAMD64()58     public void checkAMD64() {
59         assumeFalse(JavaVersionUtil.JAVA_SPEC <= 8);
60         // Test case is (currently) AMD64 only.
61         assumeTrue(getTarget().arch instanceof AMD64);
62     }
63 
64     @Test
testStringLatin1Inflate()65     public void testStringLatin1Inflate() throws ClassNotFoundException, UnsupportedEncodingException {
66         Class<?> javaclass = Class.forName("java.lang.StringLatin1");
67         Class<?> testclass = AMD64StringLatin1InflateNode.class;
68 
69         TestMethods tms = new TestMethods("testInflate", javaclass, AMD64StringLatin1InflateNode.class, "inflate",
70                         byte[].class, int.class, char[].class, int.class, int.class);
71 
72         tms.testSubstitution(testclass);
73 
74         for (int i = 0; i < N; i++) {
75             byte[] src = fillLatinBytes(new byte[i2sz(i)]);
76             char[] dst = new char[i2sz(i)];
77 
78             // Invoke void StringLatin1.inflate(byte[], 0, char[], 0, length)
79             Object nil = tms.invokeJava(src, 0, dst, 0, i2sz(i));
80 
81             assert nil == null;
82 
83             // Perform a sanity check:
84             for (int j = 0; j < i2sz(i); j++) {
85                 assert (dst[j] & 0xff00) == 0;
86                 assert (32 <= dst[j] && dst[j] <= 126) || (160 <= dst[j] && dst[j] <= 255);
87                 assert ((byte) dst[j] == src[j]);
88             }
89 
90             String str = new String(src, 0, src.length, "ISO8859_1");
91 
92             for (int j = 0; j < src.length; j++) {
93                 assert ((char) src[j] & 0xff) == str.charAt(j);
94             }
95 
96             // Invoke char[] testInflate(String)
97             char[] inflate1 = (char[]) tms.invokeTest(str);
98 
99             // Another sanity check:
100             for (int j = 0; j < i2sz(i); j++) {
101                 assert (inflate1[j] & 0xff00) == 0;
102                 assert (32 <= inflate1[j] && inflate1[j] <= 126) || (160 <= inflate1[j] && inflate1[j] <= 255);
103             }
104 
105             assertDeepEquals(dst, inflate1);
106 
107             // Invoke char[] testInflate(String) through code handle.
108             char[] inflate2 = (char[]) tms.invokeCode(str);
109             assertDeepEquals(dst, inflate2);
110         }
111     }
112 
113     @Test
testStringLatin1InflateByteByte()114     public void testStringLatin1InflateByteByte() throws ClassNotFoundException {
115         Class<?> javaclass = Class.forName("java.lang.StringLatin1");
116 
117         ResolvedJavaMethod caller = getResolvedJavaMethod(javaclass, "inflate", byte[].class, int.class, byte[].class, int.class, int.class);
118         StructuredGraph graph = getReplacements().getIntrinsicGraph(caller, CompilationIdentifier.INVALID_COMPILATION_ID, getDebugContext(), null);
119         assertInGraph(graph, AMD64StringLatin1InflateNode.class);
120 
121         InstalledCode code = getCode(caller, graph);
122 
123         for (int dstOffset = 0; dstOffset < 2; dstOffset++) {
124             for (int srcOffset = 0; srcOffset < 2; srcOffset++) {
125                 for (int i = 0; i < N; i++) {
126                     int length = i2sz(i);
127                     byte[] src = fillLatinBytes(new byte[length]);
128                     int resultLength = length * 2;
129                     byte[] dst = new byte[resultLength];
130                     int copiedLength = Math.max(0, length - Math.max(dstOffset, srcOffset));
131                     int dstDelta = Math.min(dstOffset, copiedLength);
132                     int srcDelta = Math.min(srcOffset, copiedLength);
133                     invokeSafe(caller, null, src, srcDelta, dst, dstDelta, copiedLength);
134 
135                     // Perform a sanity check:
136                     for (int j = 0; j < copiedLength; j++) {
137                         assert (dst[j * 2 + 1 + dstDelta * 2]) == 0;
138                         int c = dst[j * 2 + dstDelta * 2] & 0xFF;
139                         assert (32 <= c && c <= 126) || (160 <= c && c <= 255);
140                         assert (c == (src[j + srcDelta] & 0xFF));
141                     }
142 
143                     byte[] dst2 = new byte[resultLength];
144                     executeVarargsSafe(code, src, srcDelta, dst2, dstDelta, copiedLength);
145                     assertDeepEquals(dst, dst2);
146                 }
147             }
148         }
149     }
150 
151     @Test
testStringLatin1InflateByteChar()152     public void testStringLatin1InflateByteChar() throws ClassNotFoundException {
153         Class<?> javaclass = Class.forName("java.lang.StringLatin1");
154 
155         ResolvedJavaMethod caller = getResolvedJavaMethod(javaclass, "inflate", byte[].class, int.class, char[].class, int.class, int.class);
156         StructuredGraph graph = getReplacements().getIntrinsicGraph(caller, CompilationIdentifier.INVALID_COMPILATION_ID, getDebugContext(), null);
157         assertInGraph(graph, AMD64StringLatin1InflateNode.class);
158 
159         InstalledCode code = getCode(caller, graph);
160 
161         for (int dstOffset = 0; dstOffset < 2; dstOffset++) {
162             for (int srcOffset = 0; srcOffset < 2; srcOffset++) {
163                 for (int i = 0; i < N; i++) {
164                     int length = i2sz(i);
165                     byte[] src = fillLatinBytes(new byte[length]);
166                     char[] dst = new char[length];
167                     int copiedLength = Math.max(0, length - Math.max(dstOffset, srcOffset));
168                     int dstDelta = Math.min(dstOffset, copiedLength);
169                     int srcDelta = Math.min(srcOffset, copiedLength);
170                     invokeSafe(caller, null, src, srcDelta, dst, dstDelta, copiedLength);
171 
172                     // Perform a sanity check:
173                     for (int j = 0; j < copiedLength; j++) {
174                         int c = dst[j + dstDelta] & 0xFF;
175                         assert (32 <= c && c <= 126) || (160 <= c && c <= 255);
176                         assert (c == (src[j + srcDelta] & 0xFF));
177                     }
178 
179                     char[] dst2 = new char[length];
180                     executeVarargsSafe(code, src, srcDelta, dst2, dstDelta, copiedLength);
181                     assertDeepEquals(dst, dst2);
182                 }
183             }
184         }
185     }
186 
187     @Test
testStringUTF16Compress()188     public void testStringUTF16Compress() throws ClassNotFoundException, UnsupportedEncodingException {
189         Class<?> javaclass = Class.forName("java.lang.StringUTF16");
190         Class<?> testclass = AMD64StringUTF16CompressNode.class;
191         TestMethods tms = new TestMethods("testCompress", javaclass, AMD64StringUTF16CompressNode.class, "compress",
192                         char[].class, int.class, byte[].class, int.class, int.class);
193         tms.testSubstitution(testclass);
194 
195         for (int i = 0; i < N; i++) {
196             char[] src = fillLatinChars(new char[i2sz(i)]);
197             byte[] dst = new byte[i2sz(i)];
198 
199             // Invoke int StringUTF16.compress(char[], 0, byte[], 0, length)
200             Object len = tms.invokeJava(src, 0, dst, 0, i2sz(i));
201 
202             assert (int) len == i2sz(i);
203 
204             // Invoke String testCompress(char[])
205             String str1 = (String) tms.invokeTest(src);
206 
207             assertDeepEquals(dst, str1.getBytes("ISO8859_1"));
208 
209             // Invoke String testCompress(char[]) through code handle.
210             String str2 = (String) tms.invokeCode(src);
211 
212             assertDeepEquals(dst, str2.getBytes("ISO8859_1"));
213         }
214     }
215 
216     @Test
testStringUTF16CompressByteByte()217     public void testStringUTF16CompressByteByte() throws ClassNotFoundException {
218         Class<?> javaclass = Class.forName("java.lang.StringUTF16");
219 
220         ResolvedJavaMethod caller = getResolvedJavaMethod(javaclass, "compress", byte[].class, int.class, byte[].class, int.class, int.class);
221         StructuredGraph graph = getReplacements().getIntrinsicGraph(caller, CompilationIdentifier.INVALID_COMPILATION_ID, getDebugContext(), null);
222         assertInGraph(graph, AMD64StringUTF16CompressNode.class);
223 
224         InstalledCode code = getCode(caller, graph);
225 
226         for (int dstOffset = 0; dstOffset < 2; dstOffset++) {
227             for (int srcOffset = 0; srcOffset < 2; srcOffset++) {
228                 for (int i = 0; i < N; i++) {
229                     int length = i2sz(i);
230                     byte[] src = fillLatinChars(new byte[length * 2]);
231                     byte[] dst = new byte[length];
232                     int copiedLength = Math.max(0, length - Math.max(dstOffset, srcOffset));
233                     int dstDelta = Math.min(dstOffset, copiedLength);
234                     int srcDelta = Math.min(srcOffset, copiedLength);
235                     invokeSafe(caller, null, src, srcDelta, dst, dstDelta, copiedLength);
236 
237                     // Perform a sanity check:
238                     for (int j = 0; j < copiedLength; j++) {
239                         int c = dst[j + dstDelta] & 0xFF;
240                         assert (32 <= c && c <= 126) || (160 <= c && c <= 255);
241                         assert (c == (src[(j + srcDelta) * 2] & 0xFF));
242                     }
243 
244                     byte[] dst2 = new byte[length];
245                     executeVarargsSafe(code, src, srcDelta, dst2, dstDelta, copiedLength);
246                     assertDeepEquals(dst, dst2);
247                 }
248             }
249         }
250     }
251 
252     @Test
testStringUTF16CompressCharByte()253     public void testStringUTF16CompressCharByte() throws ClassNotFoundException {
254         Class<?> javaclass = Class.forName("java.lang.StringUTF16");
255 
256         ResolvedJavaMethod caller = getResolvedJavaMethod(javaclass, "compress", char[].class, int.class, byte[].class, int.class, int.class);
257         StructuredGraph graph = getReplacements().getIntrinsicGraph(caller, CompilationIdentifier.INVALID_COMPILATION_ID, getDebugContext(), null);
258         assertInGraph(graph, AMD64StringUTF16CompressNode.class);
259 
260         InstalledCode code = getCode(caller, graph);
261 
262         for (int dstOffset = 0; dstOffset < 2; dstOffset++) {
263             for (int srcOffset = 0; srcOffset < 2; srcOffset++) {
264                 for (int i = 0; i < N; i++) {
265                     int length = i2sz(i);
266                     char[] src = fillLatinChars(new char[length]);
267                     byte[] dst = new byte[length];
268                     int copiedLength = Math.max(0, length - Math.max(dstOffset, srcOffset));
269                     int dstDelta = Math.min(dstOffset, copiedLength);
270                     int srcDelta = Math.min(srcOffset, copiedLength);
271                     invokeSafe(caller, null, src, srcDelta, dst, dstDelta, copiedLength);
272 
273                     // Perform a sanity check:
274                     for (int j = 0; j < copiedLength; j++) {
275                         int c = dst[j + dstDelta] & 0xFF;
276                         assert (32 <= c && c <= 126) || (160 <= c && c <= 255);
277                         assert (c == (src[j + srcDelta] & 0xFF));
278                     }
279 
280                     byte[] dst2 = new byte[length];
281                     executeVarargsSafe(code, src, srcDelta, dst2, dstDelta, copiedLength);
282                     assertDeepEquals(dst, dst2);
283                 }
284             }
285         }
286     }
287 
288     @SuppressWarnings("all")
testCompress(char[] a)289     public static String testCompress(char[] a) {
290         return new String(a);
291     }
292 
293     @SuppressWarnings("all")
testInflate(String a)294     public static char[] testInflate(String a) {
295         return a.toCharArray();
296     }
297 
298     private class TestMethods {
299 
TestMethods(String testmname, Class<?> javaclass, Class<?> intrinsicClass, String javamname, Class<?>... params)300         TestMethods(String testmname, Class<?> javaclass, Class<?> intrinsicClass, String javamname, Class<?>... params) {
301             javamethod = getResolvedJavaMethod(javaclass, javamname, params);
302             testmethod = getResolvedJavaMethod(testmname);
303             testgraph = getReplacements().getIntrinsicGraph(javamethod, CompilationIdentifier.INVALID_COMPILATION_ID, getDebugContext(), null);
304             assertInGraph(testgraph, intrinsicClass);
305 
306             assert javamethod != null;
307             assert testmethod != null;
308 
309             // Force the test method to be compiled.
310             testcode = getCode(testmethod);
311 
312             assert testcode != null;
313         }
314 
replacementGraph()315         StructuredGraph replacementGraph() {
316             return getReplacements().getSubstitution(javamethod, -1, false, null, getInitialOptions());
317         }
318 
testMethodGraph()319         StructuredGraph testMethodGraph() {
320             return testgraph;
321         }
322 
testSubstitution(Class<?> intrinsicclass)323         void testSubstitution(Class<?> intrinsicclass) {
324             // Check if the resulting graph contains the expected node.
325             if (replacementGraph() == null) {
326                 assertInGraph(testMethodGraph(), intrinsicclass);
327             }
328         }
329 
invokeJava(Object... args)330         Object invokeJava(Object... args) {
331             return invokeSafe(javamethod, null, args);
332         }
333 
invokeTest(Object... args)334         Object invokeTest(Object... args) {
335             return invokeSafe(testmethod, null, args);
336         }
337 
invokeCode(Object... args)338         Object invokeCode(Object... args) {
339             return executeVarargsSafe(testcode, args);
340         }
341 
342         // Private data section:
343         private ResolvedJavaMethod javamethod;
344         private ResolvedJavaMethod testmethod;
345         private StructuredGraph testgraph;
346         private InstalledCode testcode;
347     }
348 
fillLatinBytes(byte[] v)349     private static byte[] fillLatinBytes(byte[] v) {
350         for (int ch = 32, i = 0; i < v.length; i++) {
351             v[i] = (byte) (ch & 0xff);
352             ch = ch == 126 ? 160 : (ch == 255 ? 32 : ch + 1);
353         }
354         return v;
355     }
356 
fillLatinChars(char[] v)357     private static char[] fillLatinChars(char[] v) {
358         for (int ch = 32, i = 0; i < v.length; i++) {
359             v[i] = (char) (ch & 0xff);
360             ch = ch == 126 ? 160 : (ch == 255 ? 32 : ch + 1);
361         }
362         return v;
363     }
364 
fillLatinChars(byte[] v)365     private static byte[] fillLatinChars(byte[] v) {
366         for (int ch = 32, i = 0; i < v.length; i += 2) {
367             v[i] = (byte) (ch & 0xff);
368             ch = ch == 126 ? 160 : (ch == 255 ? 32 : ch + 1);
369         }
370         return v;
371     }
372 
i2sz(int i)373     private static int i2sz(int i) {
374         return i * 3;
375     }
376 }
377