1 /*
   2  * Copyright (c) 2015, 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 package org.graalvm.compiler.hotspot.test;
  25 
  26 import static org.graalvm.compiler.core.common.util.ModuleAPI.addExports;
  27 import static org.graalvm.compiler.core.common.util.ModuleAPI.getModule;
  28 
  29 import java.lang.reflect.Method;
  30 
  31 import org.graalvm.compiler.core.common.util.Util;
  32 import org.graalvm.compiler.core.test.GraalCompilerTest;
  33 import org.graalvm.compiler.debug.Debug;
  34 import org.graalvm.compiler.debug.Debug.Scope;
  35 import org.graalvm.compiler.graph.Node;
  36 import org.graalvm.compiler.nodes.Invoke;
  37 import org.graalvm.compiler.nodes.StructuredGraph;
  38 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
  39 import org.junit.BeforeClass;
  40 import org.junit.Test;
  41 
  42 import org.objectweb.asm.ClassWriter;
  43 import org.objectweb.asm.MethodVisitor;
  44 import org.objectweb.asm.Opcodes;
  45 import jdk.vm.ci.meta.ResolvedJavaMethod;
  46 
  47 public class ConstantPoolSubstitutionsTests extends GraalCompilerTest implements Opcodes {
  48 
  49     @SuppressWarnings("try")
  50     protected StructuredGraph test(final String snippet) {
  51         ResolvedJavaMethod method = getMetaAccess().lookupJavaMethod(getMethod(snippet));
  52         try (Scope s = Debug.scope("ConstantPoolSubstitutionsTests", method)) {
  53             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
  54             compile(graph.method(), graph);
  55             assertNotInGraph(graph, Invoke.class);
  56             Debug.dump(Debug.BASIC_LOG_LEVEL, graph, snippet);
  57             return graph;
  58         } catch (Throwable e) {
  59             throw Debug.handle(e);
  60         }
  61     }
  62 
  63     protected static StructuredGraph assertNotInGraph(StructuredGraph graph, Class<?> clazz) {
  64         for (Node node : graph.getNodes()) {
  65             if (clazz.isInstance(node)) {
  66                 fail(node.toString());
  67             }
  68         }
  69         return graph;
  70     }
  71 
  72     private static Object getConstantPoolForObject() {
  73         String miscPackage = Java8OrEarlier ? "sun.misc" : "jdk.internal.misc";
  74         try {
  75             Class<?> sharedSecretsClass = Class.forName(miscPackage + ".SharedSecrets");
  76             Class<?> javaLangAccessClass = Class.forName(miscPackage + ".JavaLangAccess");
  77             Object jla = sharedSecretsClass.getDeclaredMethod("getJavaLangAccess").invoke(null);
  78             return javaLangAccessClass.getDeclaredMethod("getConstantPool", Class.class).invoke(jla, Object.class);
  79         } catch (Exception e) {
  80             throw new AssertionError(e);
  81         }
  82     }
  83 
  84     /**
  85      * Get the test methods from the generated class.
  86      */
  87     @Override
  88     protected Method getMethod(String methodName) {
  89         Class<?> cl;
  90         try {
  91             cl = LOADER.findClass(AsmLoader.NAME);
  92             addExports(cl);
  93         } catch (ClassNotFoundException e) {
  94             throw new AssertionError(e);
  95         }
  96         return getMethod(cl, methodName);
  97     }
  98 
  99     @BeforeClass
 100     public static void beforeClass() {
 101         addExports(AsmLoader.class);
 102     }
 103 
 104     /**
 105      * This test uses some API hidden by the JDK9 module system.
 106      */
 107     private static void addExports(Class<?> c) {
 108         if (!Util.Java8OrEarlier) {
 109             Object javaBaseModule = getModule.invoke(String.class);
 110             Object cModule = getModule.invoke(c);
 111             addExports.invokeStatic(javaBaseModule, "jdk.internal.reflect", cModule);
 112             addExports.invokeStatic(javaBaseModule, "jdk.internal.misc", cModule);
 113         }
 114     }
 115 
 116     /**
 117      * Disables these tests until we know how to dynamically export the {@code jdk.internal.reflect}
 118      * package from the {@code java.base} module to the unnamed module associated with
 119      * {@link AsmLoader}. Without such an export, the test fails as follows:
 120      *
 121      * <pre>
 122      * Caused by: java.lang.IllegalAccessError: class org.graalvm.compiler.hotspot.test.ConstantPoolTest
 123      * (in unnamed module @0x57599b23) cannot access class jdk.internal.reflect.ConstantPool (in
 124      * module java.base) because module java.base does not export jdk.internal.reflect to unnamed
 125      * module @0x57599b23
 126      * </pre>
 127      */
 128     private static void assumeJDK8() {
 129         // Assume.assumeTrue(Java8OrEarlier);
 130     }
 131 
 132     @Test
 133     public void testGetSize() {
 134         assumeJDK8();
 135         Object cp = getConstantPoolForObject();
 136         test("getSize", cp);
 137     }
 138 
 139     @Test
 140     public void testGetIntAt() {
 141         assumeJDK8();
 142         test("getIntAt");
 143     }
 144 
 145     @Test
 146     public void testGetLongAt() {
 147         assumeJDK8();
 148         test("getLongAt");
 149     }
 150 
 151     @Test
 152     public void testGetFloatAt() {
 153         assumeJDK8();
 154         test("getFloatAt");
 155     }
 156 
 157     @Test
 158     public void testGetDoubleAt() {
 159         assumeJDK8();
 160         test("getDoubleAt");
 161     }
 162 
 163     // @Test
 164     public void testGetUTF8At() {
 165         assumeJDK8();
 166         test("getUTF8At");
 167     }
 168 
 169     private static final String PACKAGE_NAME = ConstantPoolSubstitutionsTests.class.getPackage().getName();
 170     private static final String PACKAGE_NAME_INTERNAL = PACKAGE_NAME.replace('.', '/');
 171 
 172     private static AsmLoader LOADER = new AsmLoader(ConstantPoolSubstitutionsTests.class.getClassLoader());
 173 
 174     public static class AsmLoader extends ClassLoader {
 175         Class<?> loaded;
 176 
 177         static final String NAME = PACKAGE_NAME + ".ConstantPoolTest";
 178 
 179         public AsmLoader(ClassLoader parent) {
 180             super(parent);
 181         }
 182 
 183         @Override
 184         protected Class<?> findClass(String name) throws ClassNotFoundException {
 185             if (name.equals(NAME)) {
 186                 if (loaded != null) {
 187                     return loaded;
 188                 }
 189                 byte[] bytes = generateClass();
 190                 return (loaded = defineClass(name, bytes, 0, bytes.length));
 191             } else {
 192                 return super.findClass(name);
 193             }
 194         }
 195     }
 196 
 197     // @formatter:off
 198     /*
 199     static class ConstantPoolTest {
 200         public static int getSize(Object o) {
 201             ConstantPool cp = (ConstantPool) o;
 202             return cp.getSize();
 203         }
 204 
 205         public static int getIntAt(Object o) {
 206             ConstantPool cp = (ConstantPool) o;
 207             return cp.getIntAt(0);
 208         }
 209 
 210         public static long getLongAt(Object o) {
 211             ConstantPool cp = (ConstantPool) o;
 212             return cp.getLongAt(0);
 213         }
 214 
 215         public static float getFloatAt(Object o) {
 216             ConstantPool cp = (ConstantPool) o;
 217             return cp.getFloatAt(0);
 218         }
 219 
 220         public static double getDoubleAt(Object o) {
 221             ConstantPool cp = (ConstantPool) o;
 222             return cp.getDoubleAt(0);
 223         }
 224 
 225         public static String getUTF8At(Object o) {
 226             ConstantPool cp = (ConstantPool) o;
 227             return cp.getUTF8At(0);
 228         }
 229     }
 230     */
 231     // @formatter:on
 232     private static byte[] generateClass() {
 233 
 234         ClassWriter cw = new ClassWriter(0);
 235         MethodVisitor mv;
 236 
 237         cw.visit(52, ACC_SUPER, PACKAGE_NAME_INTERNAL + "/ConstantPoolTest", null, "java/lang/Object", null);
 238         cw.visitInnerClass(PACKAGE_NAME_INTERNAL + "/ConstantPoolTest", PACKAGE_NAME_INTERNAL + "/ConstantPoolSubstitutionsTests", "ConstantPoolTest",
 239                         ACC_STATIC);
 240         String constantPool = Java8OrEarlier ? "sun/reflect/ConstantPool" : "jdk/internal/reflect/ConstantPool";
 241 
 242         mv = cw.visitMethod(0, "<init>", "()V", null, null);
 243         mv.visitCode();
 244         mv.visitVarInsn(ALOAD, 0);
 245         mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
 246         mv.visitInsn(RETURN);
 247         mv.visitMaxs(1, 1);
 248         mv.visitEnd();
 249 
 250         mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "getSize", "(Ljava/lang/Object;)I", null, null);
 251         mv.visitCode();
 252         mv.visitVarInsn(ALOAD, 0);
 253         mv.visitTypeInsn(CHECKCAST, constantPool);
 254         mv.visitVarInsn(ASTORE, 1);
 255         mv.visitVarInsn(ALOAD, 1);
 256         mv.visitMethodInsn(INVOKEVIRTUAL, constantPool, "getSize", "()I", false);
 257         mv.visitInsn(IRETURN);
 258         mv.visitMaxs(1, 3);
 259         mv.visitEnd();
 260 
 261         mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "getIntAt", "(Ljava/lang/Object;)I", null, null);
 262         mv.visitCode();
 263         mv.visitVarInsn(ALOAD, 0);
 264         mv.visitTypeInsn(CHECKCAST, constantPool);
 265         mv.visitVarInsn(ASTORE, 1);
 266         mv.visitVarInsn(ALOAD, 1);
 267         mv.visitInsn(ICONST_0);
 268         mv.visitMethodInsn(INVOKEVIRTUAL, constantPool, "getIntAt", "(I)I", false);
 269         mv.visitInsn(IRETURN);
 270         mv.visitMaxs(2, 3);
 271         mv.visitEnd();
 272 
 273         mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "getLongAt", "(Ljava/lang/Object;)J", null, null);
 274         mv.visitCode();
 275         mv.visitVarInsn(ALOAD, 0);
 276         mv.visitTypeInsn(CHECKCAST, constantPool);
 277         mv.visitVarInsn(ASTORE, 1);
 278         mv.visitVarInsn(ALOAD, 1);
 279         mv.visitInsn(ICONST_0);
 280         mv.visitMethodInsn(INVOKEVIRTUAL, constantPool, "getLongAt", "(I)J", false);
 281         mv.visitInsn(LRETURN);
 282         mv.visitMaxs(2, 3);
 283         mv.visitEnd();
 284 
 285         mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "getFloatAt", "(Ljava/lang/Object;)F", null, null);
 286         mv.visitCode();
 287         mv.visitVarInsn(ALOAD, 0);
 288         mv.visitTypeInsn(CHECKCAST, constantPool);
 289         mv.visitVarInsn(ASTORE, 1);
 290         mv.visitVarInsn(ALOAD, 1);
 291         mv.visitInsn(ICONST_0);
 292         mv.visitMethodInsn(INVOKEVIRTUAL, constantPool, "getFloatAt", "(I)F", false);
 293         mv.visitInsn(FRETURN);
 294         mv.visitMaxs(2, 3);
 295         mv.visitEnd();
 296 
 297         mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "getDoubleAt", "(Ljava/lang/Object;)D", null, null);
 298         mv.visitCode();
 299         mv.visitVarInsn(ALOAD, 0);
 300         mv.visitTypeInsn(CHECKCAST, constantPool);
 301         mv.visitVarInsn(ASTORE, 1);
 302         mv.visitVarInsn(ALOAD, 1);
 303         mv.visitInsn(ICONST_0);
 304         mv.visitMethodInsn(INVOKEVIRTUAL, constantPool, "getDoubleAt", "(I)D", false);
 305         mv.visitInsn(DRETURN);
 306         mv.visitMaxs(2, 3);
 307         mv.visitEnd();
 308 
 309         mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "getUTF8At", "(Ljava/lang/Object;)Ljava/lang/String;", null, null);
 310         mv.visitCode();
 311         mv.visitVarInsn(ALOAD, 0);
 312         mv.visitTypeInsn(CHECKCAST, constantPool);
 313         mv.visitVarInsn(ASTORE, 1);
 314         mv.visitVarInsn(ALOAD, 1);
 315         mv.visitInsn(ICONST_0);
 316         mv.visitMethodInsn(INVOKEVIRTUAL, constantPool, "getUTF8At", "(I)Ljava/lang/String;", false);
 317         mv.visitInsn(ARETURN);
 318         mv.visitMaxs(2, 3);
 319         mv.visitEnd();
 320         cw.visitEnd();
 321 
 322         return cw.toByteArray();
 323     }
 324 }