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
  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
  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
 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
 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
 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
 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
 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")
 289     public static String testCompress(char[] a) {
 290         return new String(a);
 291     }
 292 
 293     @SuppressWarnings("all")
 294     public static char[] testInflate(String a) {
 295         return a.toCharArray();
 296     }
 297 
 298     private class TestMethods {
 299 
 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 
 315         StructuredGraph replacementGraph() {
 316             return getReplacements().getSubstitution(javamethod, -1, false, null, getInitialOptions());
 317         }
 318 
 319         StructuredGraph testMethodGraph() {
 320             return testgraph;
 321         }
 322 
 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 
 330         Object invokeJava(Object... args) {
 331             return invokeSafe(javamethod, null, args);
 332         }
 333 
 334         Object invokeTest(Object... args) {
 335             return invokeSafe(testmethod, null, args);
 336         }
 337 
 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 
 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 
 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 
 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 
 373     private static int i2sz(int i) {
 374         return i * 3;
 375     }
 376 }