0c37008917
Reviewed-by: mseledtsov, sspitsyn, lmesnik
343 lines
12 KiB
Java
343 lines
12 KiB
Java
/*
|
|
* 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<T> {
|
|
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<T> implements Test<T> {
|
|
public static final Object CORRECT = new Object();
|
|
public static final Object WRONG = new Object();
|
|
|
|
final Method TEST;
|
|
private final Class<T> declared;
|
|
private final Class<?> receiver;
|
|
|
|
private final HashMap<Integer, T> receivers = new HashMap<>();
|
|
|
|
public ATest(Class<T> 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> T compute(Callable<T> 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: <init>()V
|
|
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
|
mv.visitCode();
|
|
mv.visitVarInsn(ALOAD, 0);
|
|
mv.visitMethodInsn(INVOKESPECIAL, intl(c.getName()), "<init>", "()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<? extends Throwable> 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);
|
|
}
|
|
}
|
|
}
|