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