mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
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:
parent
5a768f75c9
commit
c6339cb8a2
78 changed files with 6118 additions and 544 deletions
|
@ -73,6 +73,7 @@ import jdk.internal.loader.BuiltinClassLoader;
|
|||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.module.Resources;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.CallerSensitiveAdapter;
|
||||
import jdk.internal.reflect.ConstantPool;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.reflect.ReflectionFactory;
|
||||
|
@ -372,9 +373,15 @@ public final class Class<T> implements java.io.Serializable,
|
|||
public static Class<?> forName(String className)
|
||||
throws ClassNotFoundException {
|
||||
Class<?> caller = Reflection.getCallerClass();
|
||||
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
|
||||
return forName(className, caller);
|
||||
}
|
||||
|
||||
// Caller-sensitive adapter method for reflective invocation
|
||||
@CallerSensitiveAdapter
|
||||
private static Class<?> forName(String className, Class<?> caller)
|
||||
throws ClassNotFoundException {
|
||||
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Class} object associated with the class or
|
||||
|
@ -456,11 +463,25 @@ public final class Class<T> implements java.io.Serializable,
|
|||
// Reflective call to get caller class is only needed if a security manager
|
||||
// is present. Avoid the overhead of making this call otherwise.
|
||||
caller = Reflection.getCallerClass();
|
||||
}
|
||||
return forName(name, initialize, loader, caller);
|
||||
}
|
||||
|
||||
// Caller-sensitive adapter method for reflective invocation
|
||||
@CallerSensitiveAdapter
|
||||
private static Class<?> forName(String name, boolean initialize, ClassLoader loader, Class<?> caller)
|
||||
throws ClassNotFoundException
|
||||
{
|
||||
@SuppressWarnings("removal")
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
// Reflective call to get caller class is only needed if a security manager
|
||||
// is present. Avoid the overhead of making this call otherwise.
|
||||
if (loader == null) {
|
||||
ClassLoader ccl = ClassLoader.getClassLoader(caller);
|
||||
if (ccl != null) {
|
||||
sm.checkPermission(
|
||||
SecurityConstants.GET_CLASSLOADER_PERMISSION);
|
||||
SecurityConstants.GET_CLASSLOADER_PERMISSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -523,13 +544,24 @@ public final class Class<T> implements java.io.Serializable,
|
|||
@SuppressWarnings("removal")
|
||||
@CallerSensitive
|
||||
public static Class<?> forName(Module module, String name) {
|
||||
Class<?> caller = null;
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
caller = Reflection.getCallerClass();
|
||||
}
|
||||
return forName(module, name, caller);
|
||||
}
|
||||
|
||||
// Caller-sensitive adapter method for reflective invocation
|
||||
@SuppressWarnings("removal")
|
||||
@CallerSensitiveAdapter
|
||||
private static Class<?> forName(Module module, String name, Class<?> caller) {
|
||||
Objects.requireNonNull(module);
|
||||
Objects.requireNonNull(name);
|
||||
|
||||
ClassLoader cl;
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
Class<?> caller = Reflection.getCallerClass();
|
||||
if (caller != null && caller.getModule() != module) {
|
||||
// if caller is null, Class.forName is the last java frame on the stack.
|
||||
// java.base has all permissions
|
||||
|
|
|
@ -64,6 +64,7 @@ import jdk.internal.perf.PerfCounter;
|
|||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.CallerSensitiveAdapter;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.util.StaticProperty;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
|
@ -1615,9 +1616,13 @@ public abstract class ClassLoader {
|
|||
*/
|
||||
@CallerSensitive
|
||||
protected static boolean registerAsParallelCapable() {
|
||||
Class<? extends ClassLoader> callerClass =
|
||||
Reflection.getCallerClass().asSubclass(ClassLoader.class);
|
||||
return ParallelLoaders.register(callerClass);
|
||||
return registerAsParallelCapable(Reflection.getCallerClass());
|
||||
}
|
||||
|
||||
// Caller-sensitive adapter method for reflective invocation
|
||||
@CallerSensitiveAdapter
|
||||
private static boolean registerAsParallelCapable(Class<?> caller) {
|
||||
return ParallelLoaders.register(caller.asSubclass(ClassLoader.class));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.misc.CDS;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import sun.invoke.util.BytecodeDescriptor;
|
||||
import sun.invoke.util.VerifyAccess;
|
||||
|
@ -37,7 +36,6 @@ import java.io.FilePermission;
|
|||
import java.io.Serializable;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
@ -46,8 +44,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
import java.util.PropertyPermission;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
|
@ -57,7 +57,6 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
* @see LambdaMetafactory
|
||||
*/
|
||||
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
|
||||
private static final int CLASSFILE_VERSION = VM.classFileVersion();
|
||||
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
|
||||
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
|
||||
private static final String NAME_CTOR = "<init>";
|
||||
|
@ -106,7 +105,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
|
||||
|
||||
// condy to load implMethod from class data
|
||||
MethodType classDataMType = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
|
||||
MethodType classDataMType = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
|
||||
Handle classDataBsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData",
|
||||
classDataMType.descriptorString(), false);
|
||||
implMethodCondy = new ConstantDynamic(ConstantDescs.DEFAULT_NAME, MethodHandle.class.descriptorString(), classDataBsm);
|
||||
|
@ -227,50 +226,28 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
@Override
|
||||
CallSite buildCallSite() throws LambdaConversionException {
|
||||
final Class<?> innerClass = spinInnerClass();
|
||||
if (factoryType.parameterCount() == 0) {
|
||||
// In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance,
|
||||
// unless we've suppressed eager initialization
|
||||
if (disableEagerInitialization) {
|
||||
try {
|
||||
return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD,
|
||||
factoryType.returnType()));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new LambdaConversionException(
|
||||
"Exception finding " + LAMBDA_INSTANCE_FIELD + " static field", e);
|
||||
}
|
||||
} else {
|
||||
@SuppressWarnings("removal")
|
||||
final Constructor<?>[] ctrs = AccessController.doPrivileged(
|
||||
new PrivilegedAction<>() {
|
||||
@Override
|
||||
public Constructor<?>[] run() {
|
||||
Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
|
||||
if (ctrs.length == 1) {
|
||||
// The lambda implementing inner class constructor is private, set
|
||||
// it accessible (by us) before creating the constant sole instance
|
||||
ctrs[0].setAccessible(true);
|
||||
}
|
||||
return ctrs;
|
||||
}
|
||||
});
|
||||
if (ctrs.length != 1) {
|
||||
throw new LambdaConversionException("Expected one lambda constructor for "
|
||||
+ innerClass.getCanonicalName() + ", got " + ctrs.length);
|
||||
}
|
||||
|
||||
try {
|
||||
Object inst = ctrs[0].newInstance();
|
||||
return new ConstantCallSite(MethodHandles.constant(interfaceClass, inst));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new LambdaConversionException("Exception instantiating lambda object", e);
|
||||
}
|
||||
if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
|
||||
try {
|
||||
return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD,
|
||||
factoryType.returnType()));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new LambdaConversionException(
|
||||
"Exception finding " + LAMBDA_INSTANCE_FIELD + " static field", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
MethodHandle mh = caller.findConstructor(innerClass, constructorType);
|
||||
return new ConstantCallSite(mh.asType(factoryType));
|
||||
if (factoryType.parameterCount() == 0) {
|
||||
// In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance
|
||||
Object inst = mh.asType(methodType(Object.class)).invokeExact();
|
||||
return new ConstantCallSite(MethodHandles.constant(interfaceClass, inst));
|
||||
} else {
|
||||
return new ConstantCallSite(mh.asType(factoryType));
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new LambdaConversionException("Exception finding constructor", e);
|
||||
} catch (Throwable e) {
|
||||
throw new LambdaConversionException("Exception instantiating lambda object", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.BasicType;
|
||||
|
@ -316,7 +317,7 @@ class InvokerBytecodeGenerator {
|
|||
* Extract the MemberName of a newly-defined method.
|
||||
*/
|
||||
private MemberName loadMethod(byte[] classFile) {
|
||||
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile)
|
||||
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile, Set.of())
|
||||
.defineClass(true, classDataValues());
|
||||
return resolveInvokerMember(invokerClass, invokerName, invokerType);
|
||||
}
|
||||
|
@ -376,7 +377,7 @@ class InvokerBytecodeGenerator {
|
|||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(Type.getType("L" + className + ";"));
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandleNatives",
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandles",
|
||||
"classData", "(Ljava/lang/Class;)Ljava/lang/Object;", false);
|
||||
// we should optimize one single element case that does not need to create a List
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/util/List");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
|
@ -35,7 +34,6 @@ import java.lang.reflect.Field;
|
|||
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
|
||||
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
/**
|
||||
|
@ -248,6 +246,7 @@ class MethodHandleNatives {
|
|||
return true;
|
||||
}
|
||||
static {
|
||||
VM.setJavaLangInvokeInited();
|
||||
assert(verifyConstants());
|
||||
}
|
||||
|
||||
|
@ -665,8 +664,7 @@ class MethodHandleNatives {
|
|||
|
||||
static boolean canBeCalledVirtual(MemberName mem) {
|
||||
assert(mem.isInvocable());
|
||||
return mem.getName().equals("getContextClassLoader") &&
|
||||
canBeCalledVirtual(mem, java.lang.Thread.class);
|
||||
return mem.getName().equals("getContextClassLoader") && canBeCalledVirtual(mem, java.lang.Thread.class);
|
||||
}
|
||||
|
||||
static boolean canBeCalledVirtual(MemberName symbolicRef, Class<?> definingClass) {
|
||||
|
@ -676,16 +674,4 @@ class MethodHandleNatives {
|
|||
return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef
|
||||
symbolicRefClass.isInterface()); // Mdef implements Msym
|
||||
}
|
||||
|
||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
/*
|
||||
* Returns the class data set by the VM in the Class::classData field.
|
||||
*
|
||||
* This is also invoked by LambdaForms as it cannot use condy via
|
||||
* MethodHandles.classData due to bootstrapping issue.
|
||||
*/
|
||||
static Object classData(Class<?> c) {
|
||||
UNSAFE.ensureClassInitialized(c);
|
||||
return JLA.classData(c);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import jdk.internal.org.objectweb.asm.ClassReader;
|
|||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.CallerSensitiveAdapter;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import sun.invoke.util.ValueConversions;
|
||||
|
@ -63,7 +64,9 @@ import java.util.stream.Stream;
|
|||
import static java.lang.invoke.LambdaForm.BasicType.V_TYPE;
|
||||
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
|
||||
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
|
||||
import static java.lang.invoke.MethodHandleStatics.newInternalError;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
/**
|
||||
|
@ -117,14 +120,15 @@ public class MethodHandles {
|
|||
}
|
||||
|
||||
/**
|
||||
* This reflected$lookup method is the alternate implementation of
|
||||
* the lookup method when being invoked by reflection.
|
||||
* This lookup method is the alternate implementation of
|
||||
* the lookup method with a leading caller class argument which is
|
||||
* non-caller-sensitive. This method is only invoked by reflection
|
||||
* and method handle.
|
||||
*/
|
||||
@CallerSensitive
|
||||
private static Lookup reflected$lookup() {
|
||||
Class<?> caller = Reflection.getCallerClass();
|
||||
@CallerSensitiveAdapter
|
||||
private static Lookup lookup(Class<?> caller) {
|
||||
if (caller.getClassLoader() == null) {
|
||||
throw newIllegalArgumentException("illegal lookupClass: "+caller);
|
||||
throw newInternalError("calling lookup() reflectively is not supported: "+caller);
|
||||
}
|
||||
return new Lookup(caller);
|
||||
}
|
||||
|
@ -329,7 +333,7 @@ public class MethodHandles {
|
|||
throw new IllegalAccessException(caller + " does not have ORIGINAL access");
|
||||
}
|
||||
|
||||
Object classdata = MethodHandleNatives.classData(caller.lookupClass());
|
||||
Object classdata = classData(caller.lookupClass());
|
||||
if (classdata == null) return null;
|
||||
|
||||
try {
|
||||
|
@ -341,6 +345,17 @@ public class MethodHandles {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the class data set by the VM in the Class::classData field.
|
||||
*
|
||||
* This is also invoked by LambdaForms as it cannot use condy via
|
||||
* MethodHandles::classData due to bootstrapping issue.
|
||||
*/
|
||||
static Object classData(Class<?> c) {
|
||||
UNSAFE.ensureClassInitialized(c);
|
||||
return SharedSecrets.getJavaLangAccess().classData(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element at the specified index in the
|
||||
* {@linkplain #classData(Lookup, String, Class) class data},
|
||||
|
@ -2359,15 +2374,16 @@ public class MethodHandles {
|
|||
|
||||
/**
|
||||
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
|
||||
* from the given bytes. No package name check on the given name.
|
||||
* from the given bytes and the given options. No package name check on the given name.
|
||||
*
|
||||
* @param name fully-qualified name that specifies the prefix of the hidden class
|
||||
* @param bytes class bytes
|
||||
* @return ClassDefiner that defines a hidden class of the given bytes.
|
||||
* @param options class options
|
||||
* @return ClassDefiner that defines a hidden class of the given bytes and options.
|
||||
*/
|
||||
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes) {
|
||||
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, Set<ClassOption> options) {
|
||||
// skip name and access flags validation
|
||||
return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), Set.of(), false);
|
||||
return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), options, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package java.lang.reflect;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.ConstructorAccessor;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
@ -63,19 +64,17 @@ import java.util.StringJoiner;
|
|||
* @since 1.1
|
||||
*/
|
||||
public final class Constructor<T> extends Executable {
|
||||
@Stable
|
||||
private Class<T> clazz;
|
||||
private int slot;
|
||||
private Class<?>[] parameterTypes;
|
||||
private Class<?>[] exceptionTypes;
|
||||
@Stable
|
||||
private int modifiers;
|
||||
private final Class<T> clazz;
|
||||
private final int slot;
|
||||
private final Class<?>[] parameterTypes;
|
||||
private final Class<?>[] exceptionTypes;
|
||||
private final int modifiers;
|
||||
// Generics and annotations support
|
||||
private transient String signature;
|
||||
private final transient String signature;
|
||||
// generic info repository; lazily initialized
|
||||
private transient ConstructorRepository genericInfo;
|
||||
private byte[] annotations;
|
||||
private byte[] parameterAnnotations;
|
||||
private final byte[] annotations;
|
||||
private final byte[] parameterAnnotations;
|
||||
|
||||
// Generics infrastructure
|
||||
// Accessor for factory
|
||||
|
@ -492,9 +491,6 @@ public final class Constructor<T> extends Executable {
|
|||
if (checkAccess)
|
||||
checkAccess(caller, clazz, clazz, modifiers);
|
||||
|
||||
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
|
||||
throw new IllegalArgumentException("Cannot reflectively create enum objects");
|
||||
|
||||
ConstructorAccessor ca = constructorAccessor; // read @Stable
|
||||
if (ca == null) {
|
||||
ca = acquireConstructorAccessor();
|
||||
|
@ -534,6 +530,7 @@ public final class Constructor<T> extends Executable {
|
|||
// synchronization will probably make the implementation more
|
||||
// scalable.
|
||||
private ConstructorAccessor acquireConstructorAccessor() {
|
||||
|
||||
// First check to see if one has been created yet, and take it
|
||||
// if so.
|
||||
Constructor<?> root = this.root;
|
||||
|
@ -542,8 +539,14 @@ public final class Constructor<T> extends Executable {
|
|||
constructorAccessor = tmp;
|
||||
} else {
|
||||
// Otherwise fabricate one and propagate it up to the root
|
||||
// Ensure the declaring class is not an Enum class.
|
||||
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
|
||||
throw new IllegalArgumentException("Cannot reflectively create enum objects");
|
||||
|
||||
tmp = reflectionFactory.newConstructorAccessor(this);
|
||||
setConstructorAccessor(tmp);
|
||||
// set the constructor accessor only if it's not using native implementation
|
||||
if (VM.isJavaLangInvokeInited())
|
||||
setConstructorAccessor(tmp);
|
||||
}
|
||||
|
||||
return tmp;
|
||||
|
|
|
@ -65,23 +65,19 @@ import sun.reflect.annotation.TypeAnnotationParser;
|
|||
*/
|
||||
public final
|
||||
class Field extends AccessibleObject implements Member {
|
||||
|
||||
@Stable
|
||||
private Class<?> clazz;
|
||||
private int slot;
|
||||
private final Class<?> clazz;
|
||||
private final int slot;
|
||||
// This is guaranteed to be interned by the VM in the 1.4
|
||||
// reflection implementation
|
||||
private String name;
|
||||
@Stable
|
||||
private Class<?> type;
|
||||
@Stable
|
||||
private int modifiers;
|
||||
private boolean trustedFinal;
|
||||
private final String name;
|
||||
private final Class<?> type;
|
||||
private final int modifiers;
|
||||
private final boolean trustedFinal;
|
||||
// Generics and annotations support
|
||||
private transient String signature;
|
||||
private final transient String signature;
|
||||
// generic info repository; lazily initialized
|
||||
private transient FieldRepository genericInfo;
|
||||
private byte[] annotations;
|
||||
private final byte[] annotations;
|
||||
// Cached field accessor created without override
|
||||
@Stable
|
||||
private FieldAccessor fieldAccessor;
|
||||
|
|
|
@ -26,7 +26,9 @@
|
|||
package java.lang.reflect;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.CallerSensitiveAdapter;
|
||||
import jdk.internal.reflect.MethodAccessor;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
@ -67,24 +69,22 @@ import java.util.StringJoiner;
|
|||
* @since 1.1
|
||||
*/
|
||||
public final class Method extends Executable {
|
||||
@Stable
|
||||
private Class<?> clazz;
|
||||
private int slot;
|
||||
private final Class<?> clazz;
|
||||
private final int slot;
|
||||
// This is guaranteed to be interned by the VM in the 1.4
|
||||
// reflection implementation
|
||||
private String name;
|
||||
private Class<?> returnType;
|
||||
private Class<?>[] parameterTypes;
|
||||
private Class<?>[] exceptionTypes;
|
||||
@Stable
|
||||
private int modifiers;
|
||||
private final String name;
|
||||
private final Class<?> returnType;
|
||||
private final Class<?>[] parameterTypes;
|
||||
private final Class<?>[] exceptionTypes;
|
||||
private final int modifiers;
|
||||
// Generics and annotations support
|
||||
private transient String signature;
|
||||
private final transient String signature;
|
||||
// generic info repository; lazily initialized
|
||||
private transient MethodRepository genericInfo;
|
||||
private byte[] annotations;
|
||||
private byte[] parameterAnnotations;
|
||||
private byte[] annotationDefault;
|
||||
private final byte[] annotations;
|
||||
private final byte[] parameterAnnotations;
|
||||
private final byte[] annotationDefault;
|
||||
@Stable
|
||||
private MethodAccessor methodAccessor;
|
||||
// For sharing of MethodAccessors. This branching structure is
|
||||
|
@ -553,20 +553,64 @@ public final class Method extends Executable {
|
|||
@ForceInline // to ensure Reflection.getCallerClass optimization
|
||||
@IntrinsicCandidate
|
||||
public Object invoke(Object obj, Object... args)
|
||||
throws IllegalAccessException, IllegalArgumentException,
|
||||
InvocationTargetException
|
||||
throws IllegalAccessException, InvocationTargetException
|
||||
{
|
||||
boolean callerSensitive = isCallerSensitive();
|
||||
Class<?> caller = null;
|
||||
if (!override || callerSensitive) {
|
||||
caller = Reflection.getCallerClass();
|
||||
}
|
||||
|
||||
// Reflection::getCallerClass filters all subclasses of
|
||||
// jdk.internal.reflect.MethodAccessorImpl and Method::invoke(Object, Object[])
|
||||
// Should not call Method::invoke(Object, Object[], Class) here
|
||||
if (!override) {
|
||||
checkAccess(caller, clazz,
|
||||
Modifier.isStatic(modifiers) ? null : obj.getClass(),
|
||||
modifiers);
|
||||
}
|
||||
MethodAccessor ma = methodAccessor; // read @Stable
|
||||
if (ma == null) {
|
||||
ma = acquireMethodAccessor();
|
||||
}
|
||||
|
||||
return callerSensitive ? ma.invoke(obj, args, caller) : ma.invoke(obj, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is to support MethodHandle calling caller-sensitive Method::invoke
|
||||
* that may invoke a caller-sensitive method in order to get the original caller
|
||||
* class (not the injected invoker).
|
||||
*
|
||||
* If this adapter is not presented, MethodHandle invoking Method::invoke
|
||||
* will get an invoker class, a hidden nestmate of the original caller class,
|
||||
* that becomes the caller class invoking Method::invoke.
|
||||
*/
|
||||
@CallerSensitiveAdapter
|
||||
private Object invoke(Object obj, Object[] args, Class<?> caller)
|
||||
throws IllegalAccessException, InvocationTargetException
|
||||
{
|
||||
boolean callerSensitive = isCallerSensitive();
|
||||
if (!override) {
|
||||
Class<?> caller = Reflection.getCallerClass();
|
||||
checkAccess(caller, clazz,
|
||||
Modifier.isStatic(modifiers) ? null : obj.getClass(),
|
||||
modifiers);
|
||||
}
|
||||
MethodAccessor ma = methodAccessor; // read volatile
|
||||
MethodAccessor ma = methodAccessor; // read @Stable
|
||||
if (ma == null) {
|
||||
ma = acquireMethodAccessor();
|
||||
}
|
||||
return ma.invoke(obj, args);
|
||||
|
||||
return callerSensitive ? ma.invoke(obj, args, caller) : ma.invoke(obj, args);
|
||||
}
|
||||
|
||||
@Stable private Boolean callerSensitive; // lazily initialize
|
||||
private boolean isCallerSensitive() {
|
||||
Boolean cs = callerSensitive;
|
||||
if (cs == null) {
|
||||
callerSensitive = cs = Reflection.isCallerSensitive(this);
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -672,8 +716,10 @@ public final class Method extends Executable {
|
|||
methodAccessor = tmp;
|
||||
} else {
|
||||
// Otherwise fabricate one and propagate it up to the root
|
||||
tmp = reflectionFactory.newMethodAccessor(this);
|
||||
setMethodAccessor(tmp);
|
||||
tmp = reflectionFactory.newMethodAccessor(this, isCallerSensitive());
|
||||
// set the method accessor only if it's not using native implementation
|
||||
if (VM.isJavaLangInvokeInited())
|
||||
setMethodAccessor(tmp);
|
||||
}
|
||||
|
||||
return tmp;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue