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