8271820: Implementation of JEP 416: Reimplement Core Reflection with Method Handle

8013527: calling MethodHandles.lookup on itself leads to errors

Co-authored-by: Peter Levart <plevart@openjdk.org>
Co-authored-by: Claes Redestad <redestad@openjdk.org>
Co-authored-by: Mandy Chung <mchung@openjdk.org>
Reviewed-by: mcimadamore, plevart, egahlin, redestad, cjplummer, alanb
This commit is contained in:
Mandy Chung 2021-10-28 18:32:50 +00:00
parent 5a768f75c9
commit c6339cb8a2
78 changed files with 6118 additions and 544 deletions

View file

@ -42,6 +42,8 @@ import sun.invoke.util.Wrapper;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Collections;
@ -50,6 +52,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Stream;
@ -57,6 +60,7 @@ import java.util.stream.Stream;
import static java.lang.invoke.LambdaForm.*;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
/**
@ -1030,6 +1034,7 @@ abstract class MethodHandleImpl {
// That way we can lazily load the code and set up the constants.
private static class BindCaller {
private static MethodType INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
private static MethodType REFLECT_INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object.class, Object[].class);
static MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
// Code in the boot layer should now be careful while creating method handles or
@ -1042,15 +1047,48 @@ abstract class MethodHandleImpl {
hostClass.getName().startsWith("java.lang.invoke."))) {
throw new InternalError(); // does not happen, and should not anyway
}
MemberName member = mh.internalMemberName();
if (member != null) {
// Look up the CSM adapter method with the same method name
// but with an additional caller class parameter. If present,
// bind the adapter's method handle with the lookup class as
// the caller class argument
MemberName csmAdapter = IMPL_LOOKUP.resolveOrNull(member.getReferenceKind(),
new MemberName(member.getDeclaringClass(),
member.getName(),
member.getMethodType().appendParameterTypes(Class.class),
member.getReferenceKind()));
if (csmAdapter != null) {
assert !csmAdapter.isCallerSensitive();
MethodHandle dmh = DirectMethodHandle.make(csmAdapter);
dmh = MethodHandles.insertArguments(dmh, dmh.type().parameterCount() - 1, hostClass);
dmh = new WrappedMember(dmh, mh.type(), member, mh.isInvokeSpecial(), hostClass);
return dmh;
}
}
// If no adapter method for CSM with an additional Class parameter
// is present, then inject an invoker class that is the caller
// invoking the method handle of the CSM
try {
return bindCallerWithInjectedInvoker(mh, hostClass);
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
private static MethodHandle bindCallerWithInjectedInvoker(MethodHandle mh, Class<?> hostClass)
throws ReflectiveOperationException
{
// For simplicity, convert mh to a varargs-like method.
MethodHandle vamh = prepareForInvoker(mh);
// Cache the result of makeInjectedInvoker once per argument class.
MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass);
MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass).invoker();
return restoreToType(bccInvoker.bindTo(vamh), mh, hostClass);
}
private static MethodHandle makeInjectedInvoker(Class<?> targetClass) {
try {
private static Class<?> makeInjectedInvoker(Class<?> targetClass) {
/*
* The invoker class defined to the same class loader as the lookup class
* but in an unnamed package so that the class bytes can be cached and
@ -1064,21 +1102,80 @@ abstract class MethodHandleImpl {
name = name.replace('/', '_');
}
Class<?> invokerClass = new Lookup(targetClass)
.makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE)
.defineClass(true);
.makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE, Set.of(NESTMATE))
.defineClass(true, targetClass);
assert checkInjectedInvoker(targetClass, invokerClass);
return IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
return invokerClass;
}
private static ClassValue<MethodHandle> CV_makeInjectedInvoker = new ClassValue<MethodHandle>() {
@Override protected MethodHandle computeValue(Class<?> hostClass) {
return makeInjectedInvoker(hostClass);
private static ClassValue<InjectedInvokerHolder> CV_makeInjectedInvoker = new ClassValue<>() {
@Override
protected InjectedInvokerHolder computeValue(Class<?> hostClass) {
return new InjectedInvokerHolder(makeInjectedInvoker(hostClass));
}
};
/*
* Returns a method handle of an invoker class injected for reflection
* implementation use with the following signature:
* reflect_invoke_V(MethodHandle mh, Object target, Object[] args)
*
* Method::invoke on a caller-sensitive method will call
* MethodAccessorImpl::invoke(Object, Object[]) through reflect_invoke_V
* target.csm(args)
* NativeMethodAccesssorImpl::invoke(target, args)
* MethodAccessImpl::invoke(target, args)
* InjectedInvoker::reflect_invoke_V(vamh, target, args);
* method::invoke(target, args)
* p.Foo::m
*
* An injected invoker class is a hidden class which has the same
* defining class loader, runtime package, and protection domain
* as the given caller class.
*/
static MethodHandle reflectiveInvoker(Class<?> caller) {
return BindCaller.CV_makeInjectedInvoker.get(caller).reflectInvoker();
}
private static final class InjectedInvokerHolder {
private final Class<?> invokerClass;
// lazily resolved and cached DMH(s) of invoke_V methods
private MethodHandle invoker;
private MethodHandle reflectInvoker;
private InjectedInvokerHolder(Class<?> invokerClass) {
this.invokerClass = invokerClass;
}
private MethodHandle invoker() {
var mh = invoker;
if (mh == null) {
try {
invoker = mh = IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
} catch (Error | RuntimeException ex) {
throw ex;
} catch (Throwable ex) {
throw new InternalError(ex);
}
}
return mh;
}
private MethodHandle reflectInvoker() {
var mh = reflectInvoker;
if (mh == null) {
try {
reflectInvoker = mh = IMPL_LOOKUP.findStatic(invokerClass, "reflect_invoke_V", REFLECT_INVOKER_MT);
} catch (Error | RuntimeException ex) {
throw ex;
} catch (Throwable ex) {
throw new InternalError(ex);
}
}
return mh;
}
}
// Adapt mh so that it can be called directly from an injected invoker:
private static MethodHandle prepareForInvoker(MethodHandle mh) {
mh = mh.asFixedArity();
@ -1115,6 +1212,8 @@ abstract class MethodHandleImpl {
MethodHandle invoker = IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
MethodHandle vamh = prepareForInvoker(MH_checkCallerClass);
return (boolean)invoker.invoke(vamh, new Object[]{ invokerClass });
} catch (Error|RuntimeException ex) {
throw ex;
} catch (Throwable ex) {
throw new InternalError(ex);
}
@ -1151,27 +1250,50 @@ abstract class MethodHandleImpl {
ClassWriter cw = new ClassWriter(0);
// private static class InjectedInvoker {
// /* this is used to wrap DMH(s) of caller-sensitive methods */
// @Hidden
// static Object invoke_V(MethodHandle vamh, Object[] args) throws Throwable {
// return vamh.invokeExact(args);
// }
// /* this is used in caller-sensitive reflective method accessor */
// @Hidden
// static Object reflect_invoke_V(MethodHandle vamh, Object target, Object[] args) throws Throwable {
// return vamh.invokeExact(target, args);
// }
// }
// }
cw.visit(CLASSFILE_VERSION, ACC_PRIVATE | ACC_SUPER, "InjectedInvoker", null, "java/lang/Object", null);
{
var mv = cw.visitMethod(ACC_STATIC, "invoke_V",
"(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;",
null, null);
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "invoke_V",
"(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;",
null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
"([Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
"([Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
cw.visitEnd();
}
cw.visitEnd();
{
var mv = cw.visitMethod(ACC_STATIC, "reflect_invoke_V",
"(Ljava/lang/invoke/MethodHandle;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
return cw.toByteArray();
}
}
@ -1503,6 +1625,48 @@ abstract class MethodHandleImpl {
public VarHandle insertCoordinates(VarHandle target, int pos, Object... values) {
return VarHandles.insertCoordinates(target, pos, values);
}
@Override
public MethodHandle unreflectConstructor(Constructor<?> ctor) throws IllegalAccessException {
return IMPL_LOOKUP.unreflectConstructor(ctor);
}
@Override
public MethodHandle unreflectField(Field field, boolean isSetter) throws IllegalAccessException {
return isSetter ? IMPL_LOOKUP.unreflectSetter(field) : IMPL_LOOKUP.unreflectGetter(field);
}
@Override
public MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws IllegalAccessException {
try {
return IMPL_LOOKUP.findVirtual(defc, name, type);
} catch (NoSuchMethodException e) {
return null;
}
}
@Override
public MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws IllegalAccessException {
try {
return IMPL_LOOKUP.findStatic(defc, name, type);
} catch (NoSuchMethodException e) {
return null;
}
}
@Override
public MethodHandle reflectiveInvoker(Class<?> caller) {
Objects.requireNonNull(caller);
return BindCaller.reflectiveInvoker(caller);
}
@Override
public Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize) {
// skip name and access flags validation
return caller.makeHiddenClassDefiner(name, bytes, Set.of()).defineClassAsLookup(initialize, classData);
}
});
}