/* * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package compiler.cha; import jdk.internal.misc.Unsafe; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.vm.annotation.DontInline; import jdk.test.whitebox.WhiteBox; import jdk.test.whitebox.code.NMethod; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.util.HashMap; import java.util.concurrent.Callable; import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_MAXS; import static jdk.internal.org.objectweb.asm.Opcodes.*; import static jdk.test.lib.Asserts.assertTrue; public class Utils { public static final Unsafe U = Unsafe.getUnsafe(); public static final WhiteBox WB = WhiteBox.getWhiteBox(); interface Test { void call(T o); T receiver(int id); default Runnable monomophic() { return () -> { call(receiver(0)); // 100% }; } default Runnable bimorphic() { return () -> { call(receiver(0)); // 50% call(receiver(1)); // 50% }; } default Runnable polymorphic() { return () -> { for (int i = 0; i < 23; i++) { call(receiver(0)); // 92% } call(receiver(1)); // 4% call(receiver(2)); // 4% }; } default Runnable megamorphic() { return () -> { call(receiver(0)); // 33% call(receiver(1)); // 33% call(receiver(2)); // 33% }; } default void load(Class... cs) { // nothing to do } default void initialize(Class... cs) { for (Class c : cs) { U.ensureClassInitialized(c); } } default void repeat(int cnt, Runnable r) { for (int i = 0; i < cnt; i++) { r.run(); } } } public static abstract class ATest implements Test { public static final Object CORRECT = new Object(); public static final Object WRONG = new Object(); final Method TEST; private final Class declared; private final Class receiver; private final HashMap receivers = new HashMap<>(); public ATest(Class declared, Class receiver) { this.declared = declared; this.receiver = receiver; TEST = compute(() -> this.getClass().getDeclaredMethod("test", declared)); } @DontInline public abstract Object test(T i) throws Throwable; public abstract void checkInvalidReceiver(); public T receiver(int id) { return receivers.computeIfAbsent(id, (i -> { try { MyClassLoader cl = (MyClassLoader) receiver.getClassLoader(); Class sub = cl.subclass(receiver, i); return (T)sub.getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new Error(e); } })); } public void compile(Runnable r) { while (!WB.isMethodCompiled(TEST)) { for (int i = 0; i < 100; i++) { r.run(); } } assertCompiled(); // record nmethod info } private NMethod prevNM = null; public void assertNotCompiled() { NMethod curNM = NMethod.get(TEST, false); assertTrue(prevNM != null); // was previously compiled assertTrue(curNM == null || prevNM.compile_id != curNM.compile_id); // either no nmethod present or recompiled prevNM = curNM; // update nmethod info } public void assertCompiled() { NMethod curNM = NMethod.get(TEST, false); assertTrue(curNM != null); // nmethod is present assertTrue(prevNM == null || prevNM.compile_id == curNM.compile_id); // no recompilations if nmethod present prevNM = curNM; // update nmethod info } @Override public void call(T i) { try { assertTrue(test(i) != WRONG); } catch (Throwable e) { throw new InternalError(e); } } public static T compute(Callable c) { try { return c.call(); } catch (Exception e) { throw new Error(e); } } public static MethodHandle findVirtualHelper(Class refc, String name, Class returnType, MethodHandles.Lookup lookup) { return compute(() -> lookup.findVirtual(refc, name, MethodType.methodType(returnType))); } } @Retention(value = RetentionPolicy.RUNTIME) public @interface TestCase {} static void run(Class test, Class enclosed) { try { for (Method m : test.getMethods()) { if (m.isAnnotationPresent(TestCase.class)) { System.out.println(m.toString()); ClassLoader cl = new MyClassLoader(enclosed); Class c = cl.loadClass(test.getName()); c.getMethod(m.getName()).invoke(c.getDeclaredConstructor().newInstance()); } } } catch (Exception e) { throw new Error(e); } } static void run(Class test) { run(test, test); } static class ObjectToStringHelper { static Object testHelper(Object o) { throw new Error("not used"); } } static class ObjectHashCodeHelper { static int testHelper(Object o) { throw new Error("not used"); } } static final class MyClassLoader extends ClassLoader { private final Class test; MyClassLoader(Class test) { this.test = test; } static String intl(String s) { return s.replace('.', '/'); } Class subclass(Class c, int id) { String name = c.getName() + id; Class sub = findLoadedClass(name); if (sub == null) { ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES); cw.visit(52, ACC_PUBLIC | ACC_SUPER, intl(name), null, intl(c.getName()), null); { // Default constructor: ()V MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, intl(c.getName()), "", "()V", false); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } byte[] classFile = cw.toByteArray(); return defineClass(name, classFile, 0, classFile.length); } return sub; } protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { try { c = getParent().loadClass(name); if (name.endsWith("ObjectToStringHelper")) { ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES); cw.visit(52, ACC_PUBLIC | ACC_SUPER, intl(name), null, "java/lang/Object", null); { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "testHelper", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEINTERFACE, intl(test.getName()) + "$I", "toString", "()Ljava/lang/String;", true); mv.visitInsn(ARETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } byte[] classFile = cw.toByteArray(); return defineClass(name, classFile, 0, classFile.length); } else if (name.endsWith("ObjectHashCodeHelper")) { ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES); cw.visit(52, ACC_PUBLIC | ACC_SUPER, intl(name), null, "java/lang/Object", null); { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "testHelper", "(Ljava/lang/Object;)I", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEINTERFACE, intl(test.getName()) + "$I", "hashCode", "()I", true); mv.visitInsn(IRETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } byte[] classFile = cw.toByteArray(); return defineClass(name, classFile, 0, classFile.length); } else if (c == test || name.startsWith(test.getName())) { try { String path = name.replace('.', '/') + ".class"; byte[] classFile = getParent().getResourceAsStream(path).readAllBytes(); return defineClass(name, classFile, 0, classFile.length); } catch (IOException e) { throw new Error(e); } } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; } } public interface RunnableWithException { void run() throws Throwable; } public static void shouldThrow(Class expectedException, RunnableWithException r) { try { r.run(); throw new AssertionError("Exception not thrown: " + expectedException.getName()); } catch (Throwable e) { if (expectedException == e.getClass()) { // success: proper exception is thrown } else { throw new Error(expectedException.getName() + " is expected", e); } } } public static MethodHandle unsafeCastMH(Class cls) { try { MethodHandle mh = MethodHandles.identity(Object.class); return MethodHandles.explicitCastArguments(mh, mh.type().changeReturnType(cls)); } catch (Throwable e) { throw new Error(e); } } }