mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +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;
|
||||
|
|
|
@ -28,8 +28,11 @@ package jdk.internal.access;
|
|||
import jdk.internal.invoke.NativeEntryPoint;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -139,4 +142,43 @@ public interface JavaLangInvokeAccess {
|
|||
* @param mh the method handle
|
||||
*/
|
||||
void ensureCustomized(MethodHandle mh);
|
||||
|
||||
/**
|
||||
* Produces a method handle unreflecting from a {@code Constructor} with
|
||||
* the trusted lookup
|
||||
*/
|
||||
MethodHandle unreflectConstructor(Constructor<?> ctor) throws IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Produces a method handle unreflecting from a {@code Field} with
|
||||
* the trusted lookup
|
||||
*/
|
||||
MethodHandle unreflectField(Field field, boolean isSetter) throws IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Produces a method handle of a virtual method with the trusted lookup.
|
||||
*/
|
||||
MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Produces a method handle of a static method with the trusted lookup.
|
||||
*/
|
||||
MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Returns a method handle of an invoker class injected for core reflection
|
||||
* implementation with the following signature:
|
||||
* reflect_invoke_V(MethodHandle mh, Object target, Object[] args)
|
||||
*
|
||||
* The invoker class is a hidden class which has the same
|
||||
* defining class loader, runtime package, and protection domain
|
||||
* as the given caller class.
|
||||
*/
|
||||
MethodHandle reflectiveInvoker(Class<?> caller);
|
||||
|
||||
/**
|
||||
* Defines a hidden class of the given name and bytes with class data.
|
||||
* The given bytes is trusted.
|
||||
*/
|
||||
Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@ import java.security.Signature;
|
|||
for this purpose, namely the loss of compile-time checking. */
|
||||
|
||||
public class SharedSecrets {
|
||||
private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
private static JavaAWTAccess javaAWTAccess;
|
||||
private static JavaAWTFontAccess javaAWTFontAccess;
|
||||
private static JavaBeansAccess javaBeansAccess;
|
||||
|
|
|
@ -27,14 +27,13 @@ package jdk.internal.misc;
|
|||
|
||||
import static java.lang.Thread.State.*;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
import sun.nio.ch.FileChannelImpl;
|
||||
|
||||
public class VM {
|
||||
|
@ -91,7 +90,19 @@ public class VM {
|
|||
* @see java.lang.System#initPhase2
|
||||
*/
|
||||
public static boolean isModuleSystemInited() {
|
||||
return VM.initLevel() >= MODULE_SYSTEM_INITED;
|
||||
return initLevel >= MODULE_SYSTEM_INITED;
|
||||
}
|
||||
|
||||
private static @Stable boolean javaLangInvokeInited;
|
||||
public static void setJavaLangInvokeInited() {
|
||||
if (javaLangInvokeInited) {
|
||||
throw new InternalError("java.lang.invoke already inited");
|
||||
}
|
||||
javaLangInvokeInited = true;
|
||||
}
|
||||
|
||||
public static boolean isJavaLangInvokeInited() {
|
||||
return javaLangInvokeInited;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Utility methods used by DirectMethodHandleAccessor and DirectConstructorHandleAccessor
|
||||
*/
|
||||
public class AccessorUtils {
|
||||
/**
|
||||
* Determines if the given exception thrown by MethodHandle::invokeExact
|
||||
* is caused by an illegal argument passed to Method::invoke or
|
||||
* Constructor::newInstance. This method inspects the stack trace of
|
||||
* the exception to detect if it is thrown by the method handle core
|
||||
* implementation or the implementation of the reflected method or constructor.
|
||||
*
|
||||
* MethodHandle::invoke throws ClassCastException if the receiver object
|
||||
* is not an instance of the declaring class of the method if the method
|
||||
* is an instance method, or if a parameter value cannot be converted
|
||||
* to the corresponding formal parameter type. It throws
|
||||
* NullPointerException if the receiver object is null if the method
|
||||
* is an instance method, or if unboxing operation of a parameter fails
|
||||
* because the parameter value is null. It throws WrongMethodTypeException
|
||||
* if the method type mismatches.
|
||||
*
|
||||
* @param accessorType the accessor class that does the method handle invocation
|
||||
* @param e ClassCastException, NullPointerException or WrongMethodTypeException
|
||||
*/
|
||||
static boolean isIllegalArgument(Class<?> accessorType, RuntimeException e) {
|
||||
assert(e instanceof ClassCastException || e instanceof NullPointerException ||
|
||||
e instanceof WrongMethodTypeException);
|
||||
|
||||
StackTraceElement[] stackTrace = e.getStackTrace();
|
||||
if (stackTrace.length == 0) {
|
||||
return false; // would this happen?
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
StackTraceElement frame = stackTrace[0];
|
||||
// Class::cast and Objects::requiresNonNull may be thrown by the implementation
|
||||
// of the reflected method/constructor. Skip them and continue.
|
||||
if ((frame.getClassName().equals("java.lang.Class") && frame.getMethodName().equals("cast"))
|
||||
|| (frame.getClassName().equals("java.util.Objects") && frame.getMethodName().equals("requiresNonNull"))) {
|
||||
i++;
|
||||
}
|
||||
for (; i < stackTrace.length; i++) {
|
||||
frame = stackTrace[i];
|
||||
String cname = frame.getClassName();
|
||||
// it's illegal argument if this exception is thrown from accessorType
|
||||
if (cname.equals(accessorType.getName())) {
|
||||
return true;
|
||||
}
|
||||
// if this exception is thrown from an unnamed module or not from java.base
|
||||
// then i.e. not from method handle core implementation
|
||||
if (frame.getModuleName() == null || !frame.getModuleName().equals("java.base")) {
|
||||
return false;
|
||||
}
|
||||
int index = cname.lastIndexOf(".");
|
||||
String pn = index > 0 ? cname.substring(0, index) : "";
|
||||
// exception thrown from java.base but not from core reflection/method handle internals
|
||||
if (!IMPL_PACKAGES.contains(pn)) {
|
||||
return false;
|
||||
}
|
||||
// If Constructor::newInstance is invoked by Method::invoke or vice versa,
|
||||
// so the exception is thrown from the implementation body of the reflected
|
||||
// method or constructor
|
||||
if ((accessorType == DirectMethodHandleAccessor.class
|
||||
&& cname.startsWith(DirectConstructorHandleAccessor.class.getName()))
|
||||
|| (accessorType == DirectConstructorHandleAccessor.class
|
||||
&& cname.startsWith(DirectMethodHandleAccessor.class.getName()))) {
|
||||
// thrown from another reflection accessor impl class
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final Set<String> IMPL_PACKAGES = Set.of(
|
||||
"java.lang.reflect",
|
||||
"java.lang.invoke",
|
||||
"jdk.internal.reflect",
|
||||
"sun.invoke.util"
|
||||
);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
|
||||
/**
|
||||
* A method annotated @CallerSensitiveAdapter is an adapter method corresponding
|
||||
* to a caller-sensitive method, which is annotated with @CallerSensitive.
|
||||
*
|
||||
* A caller-sensitive adapter is private and has the same name as its
|
||||
* corresponding caller-sensitive method with a trailing caller class parameter.
|
||||
*
|
||||
* When a caller-sensitive method is invoked via Method::invoke or MethodHandle
|
||||
* the core reflection and method handle implementation will find if
|
||||
* an @CallerSensitiveAdapter method for that CSM is present. If present,
|
||||
* the runtime will invoke the adapter method with the caller class
|
||||
* argument instead. This special calling sequence ensures that the same caller
|
||||
* class is passed to a caller-sensitive method via Method::invoke,
|
||||
* MethodHandle::invokeExact, or a mix of these methods.
|
||||
*
|
||||
* For example, CSM::returnCallerClass is a caller-sensitive method
|
||||
* with an adapter method:
|
||||
* {@code
|
||||
* class CSM {
|
||||
* @CallerSensitive
|
||||
* static Class<?> returnCallerClass() {
|
||||
* return returnCallerClass(Reflection.getCallerClass());
|
||||
* }
|
||||
* @CallerSensitiveAdapter
|
||||
* private static Class<?> returnCallerClass(Class<?> caller) {
|
||||
* return caller;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* class Test {
|
||||
* void test() throws Throwable {
|
||||
* // calling CSM::returnCallerClass via reflection
|
||||
* var csm = CSM.class.getMethod("returnCallerClass");
|
||||
* // expect Foo to be the caller class
|
||||
* var caller = csm.invoke(null);
|
||||
* assert(caller == Test.class);
|
||||
* }
|
||||
* void test2() throws Throwable {
|
||||
* // method handle for Method::invoke
|
||||
* MethodHandle mh = MethodHandles.lookup().findVirtual(Method.class, "invoke",
|
||||
* methodType(Object.class, Object.class, Object[].class));
|
||||
* var csm = CSM.class.getMethod("returnCallerClass");
|
||||
* // invoke Method::invoke via method handle and the target method
|
||||
* // being invoked reflectively is CSM::returnCallerClass
|
||||
* var caller = mh.invoke(csm, null, null);
|
||||
* assert(caller == Test.class);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*
|
||||
* Both CSM::returnCallerClass and Method::invoke can have an adapter method
|
||||
* with a caller-class parameter defined. Test::test calls CSM::returnCallerClass
|
||||
* via Method::invoke which does the stack walking to find the caller's class.
|
||||
* It will pass the caller's class directly to the adapter method for
|
||||
* CSM::returnCallerClass.
|
||||
*
|
||||
* Similarly, Test::test2 invokes the method handle of Method::invoke to
|
||||
* call CSM::returnCallerClass reflectively. In that case, MethodHandle::invokeExact
|
||||
* uses the lookup class of the Lookup object producing the method handle
|
||||
* as the caller's class, so no stack walking involved. The lookup class is Test.
|
||||
* It will invoke the adapter method of Method::invoke with Test as the caller's
|
||||
* class, which in turn invokes the adapter method of CSM::returnCallerClass
|
||||
* with Test as the caller. The calling sequence eliminates the need for
|
||||
* multiple stack walks when a caller-sensitive method is invoked reflectively.
|
||||
*
|
||||
* For caller-sensitive methods that require an exact caller class, the adapter
|
||||
* method must be defined for correctness. {@link java.lang.invoke.MethodHandles#lookup()}
|
||||
* and {@link ClassLoader#registerAsParallelCapable()} are the only two methods
|
||||
* in the JDK that need the exact caller class.
|
||||
*
|
||||
* On the other hand, for caller-sensitive methods that use the caller's class
|
||||
* for access checks or security permission checks, i.e., based on its runtime
|
||||
* package, defining loader, or protection domain, the adapter method is optional.
|
||||
*/
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target({METHOD})
|
||||
public @interface CallerSensitiveAdapter {
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.Hidden;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* MethodAccessor adapter for caller-sensitive methods which have
|
||||
* an alternate non-CSM method with the same method name but an additional
|
||||
* caller class argument.
|
||||
*
|
||||
* When a caller-sensitive method is called,
|
||||
* Method::invoke(Object target, Object[] args, Class<?> caller) will
|
||||
* be invoked with the caller class. If an adapter is present,
|
||||
* the adapter method with the caller class parameter will be called
|
||||
* instead.
|
||||
*/
|
||||
class CsMethodAccessorAdapter extends MethodAccessorImpl {
|
||||
private final Method csmAdapter;
|
||||
private final MethodAccessor accessor;
|
||||
|
||||
CsMethodAccessorAdapter(Method method, Method csmAdapter, MethodAccessor accessor) {
|
||||
assert Reflection.isCallerSensitive(method) && !Reflection.isCallerSensitive(csmAdapter);
|
||||
this.csmAdapter = csmAdapter;
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object obj, Object[] args)
|
||||
throws IllegalArgumentException, InvocationTargetException {
|
||||
throw new InternalError("caller-sensitive method invoked without explicit caller: " + csmAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ForceInline
|
||||
@Hidden
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller)
|
||||
throws IllegalArgumentException, InvocationTargetException {
|
||||
Object[] newArgs = new Object[args == null ? 1 : args.length + 1];
|
||||
newArgs[0] = caller;
|
||||
if (args != null) {
|
||||
System.arraycopy(args, 0, newArgs, 1, args.length);
|
||||
}
|
||||
return accessor.invoke(obj, newArgs);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2021, 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
|
||||
|
@ -35,7 +35,6 @@ import java.util.Objects;
|
|||
|
||||
class DelegatingConstructorAccessorImpl extends ConstructorAccessorImpl {
|
||||
// initial non-null delegate
|
||||
@Stable
|
||||
private final ConstructorAccessorImpl initialDelegate;
|
||||
// alternative delegate: starts as null;
|
||||
// only single change from null -> non-null is guaranteed
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2021, 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
|
||||
|
@ -35,10 +35,11 @@ import java.util.Objects;
|
|||
|
||||
class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
|
||||
// initial non-null delegate
|
||||
@Stable private final MethodAccessorImpl initialDelegate;
|
||||
private final MethodAccessorImpl initialDelegate;
|
||||
// alternative delegate: starts as null;
|
||||
// only single change from null -> non-null is guaranteed
|
||||
@Stable private MethodAccessorImpl altDelegate;
|
||||
@Stable
|
||||
private MethodAccessorImpl altDelegate;
|
||||
|
||||
DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
|
||||
initialDelegate = Objects.requireNonNull(delegate);
|
||||
|
@ -51,6 +52,13 @@ class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
|
|||
return delegate().invoke(obj, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller)
|
||||
throws IllegalArgumentException, InvocationTargetException
|
||||
{
|
||||
return delegate().invoke(obj, args, caller);
|
||||
}
|
||||
|
||||
private MethodAccessorImpl delegate() {
|
||||
var d = altDelegate;
|
||||
return d != null ? d : initialDelegate;
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.Hidden;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import static jdk.internal.reflect.MethodHandleAccessorFactory.SPECIALIZED_PARAM_COUNT;
|
||||
|
||||
class DirectConstructorHandleAccessor extends ConstructorAccessorImpl {
|
||||
static ConstructorAccessorImpl constructorAccessor(Constructor<?> ctor, MethodHandle target) {
|
||||
return new DirectConstructorHandleAccessor(ctor, target);
|
||||
}
|
||||
|
||||
static ConstructorAccessorImpl nativeAccessor(Constructor<?> ctor) {
|
||||
return new NativeAccessor(ctor);
|
||||
}
|
||||
|
||||
private static final int PARAM_COUNT_MASK = 0x00FF;
|
||||
private static final int NONZERO_BIT = 0x8000_0000;
|
||||
|
||||
private final int paramFlags;
|
||||
private final MethodHandle target;
|
||||
|
||||
DirectConstructorHandleAccessor(Constructor<?> ctor, MethodHandle target) {
|
||||
this.paramFlags = (ctor.getParameterCount() & PARAM_COUNT_MASK) | NONZERO_BIT;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object newInstance(Object[] args) throws InstantiationException, InvocationTargetException {
|
||||
int argc = args != null ? args.length : 0;
|
||||
// only check argument count for specialized forms
|
||||
int paramCount = paramFlags & PARAM_COUNT_MASK;
|
||||
if (paramCount <= SPECIALIZED_PARAM_COUNT && argc != paramCount) {
|
||||
throw new IllegalArgumentException("wrong number of arguments: " + argc + " expected: " + paramCount);
|
||||
}
|
||||
try {
|
||||
return invokeImpl(args);
|
||||
} catch (ClassCastException|WrongMethodTypeException e) {
|
||||
if (isIllegalArgument(e))
|
||||
throw new IllegalArgumentException("argument type mismatch", e);
|
||||
else
|
||||
throw new InvocationTargetException(e);
|
||||
} catch (NullPointerException e) {
|
||||
if (isIllegalArgument(e))
|
||||
throw new IllegalArgumentException(e);
|
||||
else
|
||||
throw new InvocationTargetException(e);
|
||||
} catch (Throwable e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isIllegalArgument(RuntimeException ex) {
|
||||
return AccessorUtils.isIllegalArgument(DirectConstructorHandleAccessor.class, ex);
|
||||
}
|
||||
|
||||
@Hidden
|
||||
@ForceInline
|
||||
Object invokeImpl(Object[] args) throws Throwable {
|
||||
return switch (paramFlags & PARAM_COUNT_MASK) {
|
||||
case 0 -> target.invokeExact();
|
||||
case 1 -> target.invokeExact(args[0]);
|
||||
case 2 -> target.invokeExact(args[0], args[1]);
|
||||
case 3 -> target.invokeExact(args[0], args[1], args[2]);
|
||||
default -> target.invokeExact(args);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the constructor via native VM reflection
|
||||
*/
|
||||
static class NativeAccessor extends ConstructorAccessorImpl {
|
||||
private final Constructor<?> ctor;
|
||||
NativeAccessor(Constructor<?> ctor) {
|
||||
this.ctor = ctor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object newInstance(Object[] args) throws InstantiationException, InvocationTargetException {
|
||||
return newInstance0(ctor, args);
|
||||
}
|
||||
private static native Object newInstance0(Constructor<?> c, Object[] args)
|
||||
throws InstantiationException, InvocationTargetException;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.Hidden;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import static java.lang.invoke.MethodType.genericMethodType;
|
||||
import static jdk.internal.reflect.MethodHandleAccessorFactory.SPECIALIZED_PARAM_COUNT;
|
||||
import static jdk.internal.reflect.MethodHandleAccessorFactory.LazyStaticHolder.JLIA;
|
||||
|
||||
class DirectMethodHandleAccessor extends MethodAccessorImpl {
|
||||
/**
|
||||
* Creates a MethodAccessorImpl for a non-native method.
|
||||
*/
|
||||
static MethodAccessorImpl methodAccessor(Method method, MethodHandle target) {
|
||||
assert !Modifier.isNative(method.getModifiers());
|
||||
|
||||
return new DirectMethodHandleAccessor(method, target, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates MethodAccessorImpl for the adapter method for a caller-sensitive method.
|
||||
* The given target method handle is the adapter method with the additional caller class
|
||||
* parameter.
|
||||
*/
|
||||
static MethodAccessorImpl callerSensitiveAdapter(Method original, MethodHandle target) {
|
||||
assert Reflection.isCallerSensitive(original);
|
||||
|
||||
// for CSM adapter method with the additional caller class parameter
|
||||
// creates the adaptive method accessor only.
|
||||
return new DirectMethodHandleAccessor(original, target, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates MethodAccessorImpl that invokes the given method via VM native reflection
|
||||
* support. This is used for native methods. It can be used for java methods
|
||||
* during early VM startup.
|
||||
*/
|
||||
static MethodAccessorImpl nativeAccessor(Method method, boolean callerSensitive) {
|
||||
return callerSensitive ? new NativeAccessor(method, findCSMethodAdapter(method))
|
||||
: new NativeAccessor(method);
|
||||
}
|
||||
|
||||
private static final int PARAM_COUNT_MASK = 0x00FF;
|
||||
private static final int HAS_CALLER_PARAM_BIT = 0x0100;
|
||||
private static final int IS_STATIC_BIT = 0x0200;
|
||||
private static final int NONZERO_BIT = 0x8000_0000;
|
||||
|
||||
private final Class<?> declaringClass;
|
||||
private final int paramCount;
|
||||
private final int flags;
|
||||
private final MethodHandle target;
|
||||
|
||||
DirectMethodHandleAccessor(Method method, MethodHandle target, boolean hasCallerParameter) {
|
||||
this.declaringClass = method.getDeclaringClass();
|
||||
this.paramCount = method.getParameterCount();
|
||||
this.flags = (hasCallerParameter ? HAS_CALLER_PARAM_BIT : 0) |
|
||||
(Modifier.isStatic(method.getModifiers()) ? IS_STATIC_BIT : 0);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ForceInline
|
||||
public Object invoke(Object obj, Object[] args) throws InvocationTargetException {
|
||||
if (!isStatic()) {
|
||||
checkReceiver(obj);
|
||||
}
|
||||
checkArgumentCount(paramCount, args);
|
||||
try {
|
||||
return invokeImpl(obj, args);
|
||||
} catch (ClassCastException | WrongMethodTypeException e) {
|
||||
if (isIllegalArgument(e)) {
|
||||
// No cause in IAE to be consistent with the old behavior
|
||||
throw new IllegalArgumentException("argument type mismatch");
|
||||
} else {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
if (isIllegalArgument(e)) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} else {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ForceInline
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller) throws InvocationTargetException {
|
||||
if (!isStatic()) {
|
||||
checkReceiver(obj);
|
||||
}
|
||||
checkArgumentCount(paramCount, args);
|
||||
try {
|
||||
return invokeImpl(obj, args, caller);
|
||||
} catch (ClassCastException | WrongMethodTypeException e) {
|
||||
if (isIllegalArgument(e)) {
|
||||
// No cause in IAE to be consistent with the old behavior
|
||||
throw new IllegalArgumentException("argument type mismatch");
|
||||
} else {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
if (isIllegalArgument(e)) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} else {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Hidden
|
||||
@ForceInline
|
||||
private Object invokeImpl(Object obj, Object[] args) throws Throwable {
|
||||
return switch (paramCount) {
|
||||
case 0 -> target.invokeExact(obj);
|
||||
case 1 -> target.invokeExact(obj, args[0]);
|
||||
case 2 -> target.invokeExact(obj, args[0], args[1]);
|
||||
case 3 -> target.invokeExact(obj, args[0], args[1], args[2]);
|
||||
default -> target.invokeExact(obj, args);
|
||||
};
|
||||
}
|
||||
|
||||
@Hidden
|
||||
@ForceInline
|
||||
private Object invokeImpl(Object obj, Object[] args, Class<?> caller) throws Throwable {
|
||||
if (hasCallerParameter()) {
|
||||
// caller-sensitive method is invoked through method with caller parameter
|
||||
return switch (paramCount) {
|
||||
case 0 -> target.invokeExact(obj, caller);
|
||||
case 1 -> target.invokeExact(obj, args[0], caller);
|
||||
case 2 -> target.invokeExact(obj, args[0], args[1], caller);
|
||||
case 3 -> target.invokeExact(obj, args[0], args[1], args[2], caller);
|
||||
default -> target.invokeExact(obj, args, caller);
|
||||
};
|
||||
} else {
|
||||
// caller-sensitive method is invoked through a per-caller invoker while
|
||||
// the target MH is always spreading the args
|
||||
var invoker = JLIA.reflectiveInvoker(caller);
|
||||
try {
|
||||
// invoke the target method handle via an invoker
|
||||
return invoker.invokeExact(target, obj, args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStatic() {
|
||||
return (flags & IS_STATIC_BIT) == IS_STATIC_BIT;
|
||||
}
|
||||
|
||||
private boolean hasCallerParameter() {
|
||||
return (flags & HAS_CALLER_PARAM_BIT) == HAS_CALLER_PARAM_BIT;
|
||||
}
|
||||
|
||||
private boolean isIllegalArgument(RuntimeException ex) {
|
||||
return AccessorUtils.isIllegalArgument(DirectMethodHandleAccessor.class, ex);
|
||||
}
|
||||
|
||||
private void checkReceiver(Object o) {
|
||||
// NOTE: will throw NullPointerException, as specified, if o is null
|
||||
if (!declaringClass.isAssignableFrom(o.getClass())) {
|
||||
throw new IllegalArgumentException("object is not an instance of declaring class");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the method via native VM reflection
|
||||
*/
|
||||
static class NativeAccessor extends MethodAccessorImpl {
|
||||
private final Method method;
|
||||
private final Method csmAdapter;
|
||||
private final boolean callerSensitive;
|
||||
NativeAccessor(Method method) {
|
||||
assert !Reflection.isCallerSensitive(method);
|
||||
this.method = method;
|
||||
this.csmAdapter = null;
|
||||
this.callerSensitive = false;
|
||||
}
|
||||
|
||||
NativeAccessor(Method method, Method csmAdapter) {
|
||||
assert Reflection.isCallerSensitive(method);
|
||||
this.method = method;
|
||||
this.csmAdapter = csmAdapter;
|
||||
this.callerSensitive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object obj, Object[] args) throws InvocationTargetException {
|
||||
assert csmAdapter == null;
|
||||
return invoke0(method, obj, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller) throws InvocationTargetException {
|
||||
assert callerSensitive;
|
||||
|
||||
if (csmAdapter != null) {
|
||||
Object[] newArgs = new Object[csmAdapter.getParameterCount()];
|
||||
newArgs[0] = caller;
|
||||
if (args != null) {
|
||||
System.arraycopy(args, 0, newArgs, 1, args.length);
|
||||
}
|
||||
return invoke0(csmAdapter, obj, newArgs);
|
||||
} else {
|
||||
assert VM.isJavaLangInvokeInited();
|
||||
try {
|
||||
return ReflectiveInvoker.invoke(methodAccessorInvoker(), caller, obj, args);
|
||||
} catch (InvocationTargetException|RuntimeException|Error e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object invokeViaReflectiveInvoker(Object obj, Object[] args) throws InvocationTargetException {
|
||||
return invoke0(method, obj, args);
|
||||
}
|
||||
|
||||
/*
|
||||
* A method handle to invoke Reflective::Invoker
|
||||
*/
|
||||
private MethodHandle maInvoker;
|
||||
private MethodHandle methodAccessorInvoker() {
|
||||
MethodHandle invoker = maInvoker;
|
||||
if (invoker == null) {
|
||||
maInvoker = invoker = ReflectiveInvoker.bindTo(this);
|
||||
}
|
||||
return invoker;
|
||||
}
|
||||
|
||||
private static native Object invoke0(Method m, Object obj, Object[] args);
|
||||
|
||||
static class ReflectiveInvoker {
|
||||
/**
|
||||
* Return a method handle for NativeAccessor::invoke bound to the given accessor object
|
||||
*/
|
||||
static MethodHandle bindTo(NativeAccessor accessor) {
|
||||
return NATIVE_ACCESSOR_INVOKE.bindTo(accessor);
|
||||
}
|
||||
|
||||
/*
|
||||
* When Method::invoke on a caller-sensitive method is to be invoked
|
||||
* and no adapter method with an additional caller class argument is defined,
|
||||
* the caller-sensitive method must be invoked via an invoker injected
|
||||
* which has the following signature:
|
||||
* reflect_invoke_V(MethodHandle mh, Object target, Object[] args)
|
||||
*
|
||||
* The stack frames calling the method `csm` through reflection will
|
||||
* look like this:
|
||||
* obj.csm(args)
|
||||
* NativeAccessor::invoke(obj, args)
|
||||
* InjectedInvoker::reflect_invoke_V(vamh, obj, args);
|
||||
* method::invoke(obj, 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.
|
||||
*
|
||||
* The caller-sensitive method will call Reflection::getCallerClass
|
||||
* to get the caller class.
|
||||
*/
|
||||
static Object invoke(MethodHandle target, Class<?> caller, Object obj, Object[] args)
|
||||
throws InvocationTargetException
|
||||
{
|
||||
var reflectInvoker = JLIA.reflectiveInvoker(caller);
|
||||
try {
|
||||
return reflectInvoker.invokeExact(target, obj, args);
|
||||
} catch (InvocationTargetException | RuntimeException | Error e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
static final JavaLangInvokeAccess JLIA;
|
||||
static final MethodHandle NATIVE_ACCESSOR_INVOKE;
|
||||
static {
|
||||
try {
|
||||
JLIA = SharedSecrets.getJavaLangInvokeAccess();
|
||||
NATIVE_ACCESSOR_INVOKE = MethodHandles.lookup().findVirtual(NativeAccessor.class, "invoke",
|
||||
genericMethodType(1, true));
|
||||
} catch (NoSuchMethodException|IllegalAccessException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkArgumentCount(int paramCount, Object[] args) {
|
||||
// only check argument count for specialized forms
|
||||
if (paramCount > SPECIALIZED_PARAM_COUNT) return;
|
||||
|
||||
int argc = args != null ? args.length : 0;
|
||||
if (argc != paramCount) {
|
||||
throw new IllegalArgumentException("wrong number of arguments: " + argc + " expected: " + paramCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an adapter for caller-sensitive method if present.
|
||||
* Otherwise, null.
|
||||
*
|
||||
* A trusted method can define an adapter method for a caller-sensitive method `foo`
|
||||
* with an additional caller class argument that will be invoked reflectively.
|
||||
*/
|
||||
private static Method findCSMethodAdapter(Method method) {
|
||||
if (!Reflection.isCallerSensitive(method)) return null;
|
||||
|
||||
int paramCount = method.getParameterCount();
|
||||
Class<?>[] ptypes = new Class<?>[paramCount+1];
|
||||
ptypes[paramCount] = Class.class;
|
||||
System.arraycopy(method.getParameterTypes(), 0, ptypes, 0, paramCount);
|
||||
try {
|
||||
return method.getDeclaringClass().getDeclaredMethod(method.getName(), ptypes);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2021, 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
|
||||
|
@ -25,9 +25,8 @@
|
|||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
/** Package-private implementation of the FieldAccessor interface
|
||||
which has access to all classes and all fields, regardless of
|
||||
|
@ -35,7 +34,6 @@ import java.lang.reflect.Field;
|
|||
|
||||
abstract class FieldAccessorImpl extends MagicAccessorImpl
|
||||
implements FieldAccessor {
|
||||
@Stable
|
||||
protected final Field field;
|
||||
|
||||
FieldAccessorImpl(Field field) {
|
||||
|
@ -113,4 +111,167 @@ abstract class FieldAccessorImpl extends MagicAccessorImpl
|
|||
/** Matches specification in {@link java.lang.reflect.Field} */
|
||||
public abstract void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException;
|
||||
|
||||
|
||||
protected void ensureObj(Object o) {
|
||||
// NOTE: will throw NullPointerException, as specified, if o is null
|
||||
if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
|
||||
throwSetIllegalArgumentException(o);
|
||||
}
|
||||
}
|
||||
|
||||
private String getQualifiedFieldName() {
|
||||
return field.getDeclaringClass().getName() + "." +field.getName();
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetIllegalArgumentException(String type) {
|
||||
return new IllegalArgumentException(
|
||||
"Attempt to get "+field.getType().getName()+" field \"" +
|
||||
getQualifiedFieldName() + "\" with illegal data type conversion to "+type
|
||||
);
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(String attemptedType,
|
||||
String attemptedValue)
|
||||
throws IllegalAccessException {
|
||||
throw new IllegalAccessException(getSetMessage(attemptedType, attemptedValue));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(Object o) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException(o != null ? o.getClass().getName() : "", "");
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(boolean z) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("boolean", Boolean.toString(z));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(char b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("char", Character.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(byte b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("byte", Byte.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(short b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("short", Short.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(int i) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("int", Integer.toString(i));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(long i) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("long", Long.toString(i));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(float f) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("float", Float.toString(f));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(double f) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("double", Double.toString(f));
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetBooleanIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("boolean");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetByteIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("byte");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetCharIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("char");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetShortIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("short");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetIntIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("int");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetLongIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("long");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetFloatIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("float");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetDoubleIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("double");
|
||||
}
|
||||
|
||||
protected String getSetMessage(String attemptedType, String attemptedValue) {
|
||||
String err = "Can not set";
|
||||
if (Modifier.isStatic(field.getModifiers()))
|
||||
err += " static";
|
||||
if (Modifier.isFinal(field.getModifiers()))
|
||||
err += " final";
|
||||
err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " to ";
|
||||
if (!attemptedValue.isEmpty()) {
|
||||
err += "(" + attemptedType + ")" + attemptedValue;
|
||||
} else {
|
||||
if (!attemptedType.isEmpty())
|
||||
err += attemptedType;
|
||||
else
|
||||
err += "null value";
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
protected String getMessage(boolean getter, String attemptedType) {
|
||||
String err = "Can not " + (getter ? "get" : "set");
|
||||
if (Modifier.isStatic(field.getModifiers()))
|
||||
err += " static";
|
||||
if (Modifier.isFinal(field.getModifiers()))
|
||||
err += " final";
|
||||
err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " on " + attemptedType;
|
||||
return err;
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(String attemptedType,
|
||||
String attemptedValue) {
|
||||
throw new IllegalArgumentException(getSetMessage(attemptedType,attemptedValue));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(Object o) {
|
||||
throwSetIllegalArgumentException(o != null ? o.getClass().getName() : "", "");
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(boolean b) {
|
||||
throwSetIllegalArgumentException("boolean", Boolean.toString(b));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(byte b) {
|
||||
throwSetIllegalArgumentException("byte", Byte.toString(b));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(char c) {
|
||||
throwSetIllegalArgumentException("char", Character.toString(c));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(short s) {
|
||||
throwSetIllegalArgumentException("short", Short.toString(s));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(int i) {
|
||||
throwSetIllegalArgumentException("int", Integer.toString(i));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(long l) {
|
||||
throwSetIllegalArgumentException("long", Long.toString(l));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(float f) {
|
||||
throwSetIllegalArgumentException("float", Float.toString(f));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(double d) {
|
||||
throwSetIllegalArgumentException("double", Double.toString(d));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,4 +37,7 @@ public interface MethodAccessor {
|
|||
/** Matches specification in {@link java.lang.reflect.Method} */
|
||||
public Object invoke(Object obj, Object[] args)
|
||||
throws IllegalArgumentException, InvocationTargetException;
|
||||
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller)
|
||||
throws IllegalArgumentException, InvocationTargetException;
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ package jdk.internal.reflect;
|
|||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/** Generator for sun.reflect.MethodAccessor and
|
||||
sun.reflect.ConstructorAccessor objects using bytecodes to
|
||||
/** Generator for jdk.internal.reflect.MethodAccessor and
|
||||
jdk.internal.reflect.ConstructorAccessor objects using bytecodes to
|
||||
implement reflection. A java.lang.reflect.Method or
|
||||
java.lang.reflect.Constructor object can delegate its invoke or
|
||||
newInstance method to an accessor using native code or to one
|
||||
|
|
|
@ -45,4 +45,9 @@ abstract class MethodAccessorImpl extends MagicAccessorImpl
|
|||
/** Matches specification in {@link java.lang.reflect.Method} */
|
||||
public abstract Object invoke(Object obj, Object[] args)
|
||||
throws IllegalArgumentException, InvocationTargetException;
|
||||
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller)
|
||||
throws IllegalArgumentException, InvocationTargetException {
|
||||
return invoke(obj, args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.misc.VM;
|
||||
|
||||
import static java.lang.invoke.MethodType.genericMethodType;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
import static jdk.internal.reflect.MethodHandleAccessorFactory.LazyStaticHolder.*;
|
||||
|
||||
final class MethodHandleAccessorFactory {
|
||||
/**
|
||||
* Creates a MethodAccessor for the given reflected method.
|
||||
*
|
||||
* If the given method is called before the java.lang.invoke initialization
|
||||
* or the given method is a native method, it will use the native VM reflection
|
||||
* support.
|
||||
*
|
||||
* If the given method is a caller-sensitive method and the corresponding
|
||||
* caller-sensitive adapter with the caller class parameter is present,
|
||||
* it will use the method handle of the caller-sensitive adapter.
|
||||
*
|
||||
* Otherwise, it will use the direct method handle of the given method.
|
||||
*
|
||||
* @see CallerSensitive
|
||||
* @see CallerSensitiveAdapter
|
||||
*/
|
||||
static MethodAccessorImpl newMethodAccessor(Method method, boolean callerSensitive) {
|
||||
if (useNativeAccessor(method)) {
|
||||
return DirectMethodHandleAccessor.nativeAccessor(method, callerSensitive);
|
||||
}
|
||||
|
||||
// ExceptionInInitializerError may be thrown during class initialization
|
||||
// Ensure class initialized outside the invocation of method handle
|
||||
// so that EIIE is propagated (not wrapped with ITE)
|
||||
ensureClassInitialized(method.getDeclaringClass());
|
||||
|
||||
try {
|
||||
if (callerSensitive) {
|
||||
var dmh = findCallerSensitiveAdapter(method);
|
||||
if (dmh != null) {
|
||||
return DirectMethodHandleAccessor.callerSensitiveAdapter(method, dmh);
|
||||
}
|
||||
}
|
||||
var dmh = getDirectMethod(method, callerSensitive);
|
||||
return DirectMethodHandleAccessor.methodAccessor(method, dmh);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ConstructorAccessor for the given reflected constructor.
|
||||
*
|
||||
* If a given constructor is called before the java.lang.invoke initialization,
|
||||
* it will use the native VM reflection support.
|
||||
*
|
||||
* Otherwise, it will use the direct method handle of the given constructor.
|
||||
*/
|
||||
static ConstructorAccessorImpl newConstructorAccessor(Constructor<?> ctor) {
|
||||
if (useNativeAccessor(ctor)) {
|
||||
return DirectConstructorHandleAccessor.nativeAccessor(ctor);
|
||||
}
|
||||
|
||||
// ExceptionInInitializerError may be thrown during class initialization
|
||||
// Ensure class initialized outside the invocation of method handle
|
||||
// so that EIIE is propagated (not wrapped with ITE)
|
||||
ensureClassInitialized(ctor.getDeclaringClass());
|
||||
|
||||
try {
|
||||
MethodHandle mh = JLIA.unreflectConstructor(ctor);
|
||||
int paramCount = mh.type().parameterCount();
|
||||
MethodHandle target = mh.asFixedArity();
|
||||
MethodType mtype = specializedMethodTypeForConstructor(paramCount);
|
||||
if (paramCount > SPECIALIZED_PARAM_COUNT) {
|
||||
// spread the parameters only for the non-specialized case
|
||||
target = target.asSpreader(Object[].class, paramCount);
|
||||
}
|
||||
target = target.asType(mtype);
|
||||
return DirectConstructorHandleAccessor.constructorAccessor(ctor, target);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a FieldAccessor for the given reflected field.
|
||||
*
|
||||
* Limitation: Field access via core reflection is only supported after
|
||||
* java.lang.invoke completes initialization.
|
||||
* java.lang.invoke initialization starts soon after System::initPhase1
|
||||
* and method handles are ready for use when initPhase2 begins.
|
||||
* During early VM startup (initPhase1), fields can be accessed directly
|
||||
* from the VM or through JNI.
|
||||
*/
|
||||
static FieldAccessorImpl newFieldAccessor(Field field, boolean isReadOnly) {
|
||||
if (!VM.isJavaLangInvokeInited()) {
|
||||
throw new InternalError(field.getDeclaringClass().getName() + "::" + field.getName() +
|
||||
" cannot be accessed reflectively before java.lang.invoke is initialized");
|
||||
}
|
||||
|
||||
// ExceptionInInitializerError may be thrown during class initialization
|
||||
// Ensure class initialized outside the invocation of method handle
|
||||
// so that EIIE is propagated (not wrapped with ITE)
|
||||
ensureClassInitialized(field.getDeclaringClass());
|
||||
|
||||
try {
|
||||
// the declaring class of the field has been initialized
|
||||
var getter = JLIA.unreflectField(field, false);
|
||||
var setter = isReadOnly ? null : JLIA.unreflectField(field, true);
|
||||
Class<?> type = field.getType();
|
||||
if (type == Boolean.TYPE) {
|
||||
return MethodHandleBooleanFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Byte.TYPE) {
|
||||
return MethodHandleByteFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Short.TYPE) {
|
||||
return MethodHandleShortFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Character.TYPE) {
|
||||
return MethodHandleCharacterFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Integer.TYPE) {
|
||||
return MethodHandleIntegerFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Long.TYPE) {
|
||||
return MethodHandleLongFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Float.TYPE) {
|
||||
return MethodHandleFloatFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Double.TYPE) {
|
||||
return MethodHandleDoubleFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else {
|
||||
return MethodHandleObjectFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodHandle getDirectMethod(Method method, boolean callerSensitive) throws IllegalAccessException {
|
||||
var mtype = methodType(method.getReturnType(), method.getParameterTypes());
|
||||
var isStatic = Modifier.isStatic(method.getModifiers());
|
||||
var dmh = isStatic ? JLIA.findStatic(method.getDeclaringClass(), method.getName(), mtype)
|
||||
: JLIA.findVirtual(method.getDeclaringClass(), method.getName(), mtype);
|
||||
if (callerSensitive) {
|
||||
// the reflectiveInvoker for caller-sensitive method expects the same signature
|
||||
// as Method::invoke i.e. (Object, Object[])Object
|
||||
return makeTarget(dmh, isStatic, false);
|
||||
}
|
||||
return makeSpecializedTarget(dmh, isStatic, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the method handle of a caller-sensitive adapter for the given
|
||||
* caller-sensitive method. It has the same name as the given method
|
||||
* with a trailing caller class parameter.
|
||||
*
|
||||
* @see CallerSensitiveAdapter
|
||||
*/
|
||||
private static MethodHandle findCallerSensitiveAdapter(Method method) throws IllegalAccessException {
|
||||
String name = method.getName();
|
||||
// append a Class parameter
|
||||
MethodType mtype = methodType(method.getReturnType(), method.getParameterTypes())
|
||||
.appendParameterTypes(Class.class);
|
||||
boolean isStatic = Modifier.isStatic(method.getModifiers());
|
||||
|
||||
MethodHandle dmh = isStatic ? JLIA.findStatic(method.getDeclaringClass(), name, mtype)
|
||||
: JLIA.findVirtual(method.getDeclaringClass(), name, mtype);
|
||||
return dmh != null ? makeSpecializedTarget(dmh, isStatic, true) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the given dmh to a specialized target method handle.
|
||||
*
|
||||
* If {@code hasCallerParameter} parameter is true, transform the method handle
|
||||
* of this method type: {@code (Object, Object[], Class)Object} for the default
|
||||
* case.
|
||||
*
|
||||
* If {@code hasCallerParameter} parameter is false, transform the method handle
|
||||
* of this method type: {@code (Object, Object[])Object} for the default case.
|
||||
*
|
||||
* If the number of formal arguments is small, use a method type specialized
|
||||
* the number of formal arguments is 0, 1, and 2, for example, the method type
|
||||
* of a static method with one argument can be: {@code (Object)Object}
|
||||
*
|
||||
* If it's a static method, there is no leading Object parameter.
|
||||
*
|
||||
* @apiNote
|
||||
* This implementation avoids using MethodHandles::catchException to help
|
||||
* cold startup performance since this combination is very costly to setup.
|
||||
*
|
||||
* @param dmh DirectMethodHandle
|
||||
* @param isStatic whether given dmh represents static method or not
|
||||
* @param hasCallerParameter whether given dmh represents a method with an
|
||||
* additional caller Class parameter
|
||||
* @return transformed dmh to be used as a target in direct method accessors
|
||||
*/
|
||||
static MethodHandle makeSpecializedTarget(MethodHandle dmh, boolean isStatic, boolean hasCallerParameter) {
|
||||
MethodHandle target = dmh.asFixedArity();
|
||||
|
||||
// number of formal arguments to the original method (not the adapter)
|
||||
// If it is a non-static method, it has a leading `this` argument.
|
||||
// Also do not count the caller class argument
|
||||
int paramCount = dmh.type().parameterCount() - (isStatic ? 0 : 1) - (hasCallerParameter ? 1 : 0);
|
||||
MethodType mtype = specializedMethodType(isStatic, hasCallerParameter, paramCount);
|
||||
if (paramCount > SPECIALIZED_PARAM_COUNT) {
|
||||
int spreadArgPos = isStatic ? 0 : 1;
|
||||
target = target.asSpreader(spreadArgPos, Object[].class, paramCount);
|
||||
}
|
||||
if (isStatic) {
|
||||
// add leading 'this' parameter to static method which is then ignored
|
||||
target = MethodHandles.dropArguments(target, 0, Object.class);
|
||||
}
|
||||
return target.asType(mtype);
|
||||
}
|
||||
|
||||
// specialize for number of formal arguments <= 3 to avoid spreader
|
||||
static final int SPECIALIZED_PARAM_COUNT = 3;
|
||||
static MethodType specializedMethodType(boolean isStatic, boolean hasCallerParameter, int paramCount) {
|
||||
return switch (paramCount) {
|
||||
case 0 -> hasCallerParameter ? methodType(Object.class, Object.class, Class.class)
|
||||
: genericMethodType(1);
|
||||
case 1 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Class.class)
|
||||
: genericMethodType(2);
|
||||
case 2 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Object.class, Class.class)
|
||||
: genericMethodType(3);
|
||||
case 3 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Object.class, Object.class, Class.class)
|
||||
: genericMethodType(4);
|
||||
default -> hasCallerParameter ? methodType(Object.class, Object.class, Object[].class, Class.class)
|
||||
: genericMethodType(1, true);
|
||||
};
|
||||
}
|
||||
|
||||
static MethodType specializedMethodTypeForConstructor(int paramCount) {
|
||||
return switch (paramCount) {
|
||||
case 0 -> genericMethodType(0);
|
||||
case 1 -> genericMethodType(1);
|
||||
case 2 -> genericMethodType(2);
|
||||
case 3 -> genericMethodType(3);
|
||||
default -> genericMethodType(0, true);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the given dmh into a target method handle with the method type
|
||||
* {@code (Object, Object[])Object} or {@code (Object, Class, Object[])Object}
|
||||
*/
|
||||
static MethodHandle makeTarget(MethodHandle dmh, boolean isStatic, boolean hasCallerParameter) {
|
||||
MethodType mtype = hasCallerParameter
|
||||
? methodType(Object.class, Object.class, Object[].class, Class.class)
|
||||
: genericMethodType(1, true);
|
||||
// number of formal arguments
|
||||
int paramCount = dmh.type().parameterCount() - (isStatic ? 0 : 1) - (hasCallerParameter ? 1 : 0);
|
||||
int spreadArgPos = isStatic ? 0 : 1;
|
||||
MethodHandle target = dmh.asFixedArity().asSpreader(spreadArgPos, Object[].class, paramCount);
|
||||
if (isStatic) {
|
||||
// add leading 'this' parameter to static method which is then ignored
|
||||
target = MethodHandles.dropArguments(target, 0, Object.class);
|
||||
}
|
||||
return target.asType(mtype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the given class is initialized. If this is called from <clinit>,
|
||||
* this method returns but defc's class initialization is not completed.
|
||||
*/
|
||||
static void ensureClassInitialized(Class<?> defc) {
|
||||
if (UNSAFE.shouldBeInitialized(defc)) {
|
||||
UNSAFE.ensureClassInitialized(defc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if NativeAccessor should be used.
|
||||
*/
|
||||
private static boolean useNativeAccessor(Executable member) {
|
||||
if (!VM.isJavaLangInvokeInited())
|
||||
return true;
|
||||
|
||||
if (Modifier.isNative(member.getModifiers()))
|
||||
return true;
|
||||
|
||||
if (ReflectionFactory.useNativeAccessorOnly()) // for testing only
|
||||
return true;
|
||||
|
||||
// MethodHandle::withVarargs on a member with varargs modifier bit set
|
||||
// verifies that the last parameter of the member must be an array type.
|
||||
// The JVMS does not require the last parameter descriptor of the method descriptor
|
||||
// is an array type if the ACC_VARARGS flag is set in the access_flags item.
|
||||
// Hence the reflection implementation does not check the last parameter type
|
||||
// if ACC_VARARGS flag is set. Workaround this by invoking through
|
||||
// the native accessor.
|
||||
int paramCount = member.getParameterCount();
|
||||
if (member.isVarArgs() &&
|
||||
(paramCount == 0 || !(member.getParameterTypes()[paramCount-1].isArray()))) {
|
||||
return true;
|
||||
}
|
||||
// A method handle cannot be created if its type has an arity >= 255
|
||||
// as the method handle's invoke method consumes an extra argument
|
||||
// of the method handle itself. Fall back to use the native implementation.
|
||||
if (slotCount(member) >= MAX_JVM_ARITY) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final int MAX_JVM_ARITY = 255; // this is mandated by the JVM spec.
|
||||
/*
|
||||
* Return number of slots of the given member.
|
||||
* - long/double args counts for two argument slots
|
||||
* - A non-static method consumes an extra argument for the object on which
|
||||
* the method is called.
|
||||
* - A constructor consumes an extra argument for the object which is being constructed.
|
||||
*/
|
||||
private static int slotCount(Executable member) {
|
||||
int slots = 0;
|
||||
Class<?>[] ptypes = member.getParameterTypes();
|
||||
for (Class<?> ptype : ptypes) {
|
||||
if (ptype == double.class || ptype == long.class) {
|
||||
slots++;
|
||||
}
|
||||
}
|
||||
return ptypes.length + slots +
|
||||
(Modifier.isStatic(member.getModifiers()) ? 0 : 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delay initializing these static fields until java.lang.invoke is fully initialized.
|
||||
*/
|
||||
static class LazyStaticHolder {
|
||||
static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
|
||||
}
|
||||
|
||||
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleBooleanFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(boolean.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, boolean.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(boolean.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, boolean.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleBooleanFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleBooleanFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Boolean.valueOf(getBoolean(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
ensureObj(obj);
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (boolean) getter.invokeExact();
|
||||
} else {
|
||||
return (boolean) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
throw newGetIntIllegalArgumentException();
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
throw newGetLongIllegalArgumentException();
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
throw newGetFloatIllegalArgumentException();
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
throw newGetDoubleIllegalArgumentException();
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
ensureObj(obj);
|
||||
if (isReadOnly()) {
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Boolean b) {
|
||||
setBoolean(obj, b.booleanValue());
|
||||
} else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
ensureObj(obj);
|
||||
if (isReadOnly()) {
|
||||
throwFinalFieldIllegalAccessException(z);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(z);
|
||||
} else {
|
||||
setter.invokeExact(obj, z);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleByteFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(byte.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, byte.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(byte.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, byte.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleByteFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleByteFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Byte.valueOf(getByte(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (byte) getter.invokeExact();
|
||||
} else {
|
||||
return (byte) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
return getByte(obj);
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
return getByte(obj);
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
return getByte(obj);
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
return getByte(obj);
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getByte(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
ensureObj(obj);
|
||||
if (isReadOnly()) {
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setByte(obj, b.byteValue());
|
||||
} else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
ensureObj(obj);
|
||||
if (isReadOnly()) {
|
||||
throwFinalFieldIllegalAccessException(b);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(b);
|
||||
} else {
|
||||
setter.invokeExact(obj, b);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleCharacterFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(char.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, char.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(char.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, char.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleCharacterFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleCharacterFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Character.valueOf(getChar(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (char) getter.invokeExact();
|
||||
} else {
|
||||
return (char) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
return getChar(obj);
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
return getChar(obj);
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
return getChar(obj);
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getChar(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Character c) {
|
||||
setChar(obj, c.charValue());
|
||||
} else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(c);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(c);
|
||||
} else {
|
||||
setter.invokeExact(obj, c);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleDoubleFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(double.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, double.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(double.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, double.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleDoubleFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleDoubleFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Double.valueOf(getDouble(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
throw newGetIntIllegalArgumentException();
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
throw newGetLongIllegalArgumentException();
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
throw newGetFloatIllegalArgumentException();
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (double) getter.invokeExact();
|
||||
} else {
|
||||
return (double) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setDouble(obj, b.byteValue());
|
||||
}
|
||||
else if (value instanceof Short s) {
|
||||
setDouble(obj, s.shortValue());
|
||||
}
|
||||
else if (value instanceof Character c) {
|
||||
setDouble(obj, c.charValue());
|
||||
}
|
||||
else if (value instanceof Integer i) {
|
||||
setDouble(obj, i.intValue());
|
||||
}
|
||||
else if (value instanceof Long l) {
|
||||
setDouble(obj, l.longValue());
|
||||
}
|
||||
else if (value instanceof Float f) {
|
||||
setDouble(obj, f.floatValue());
|
||||
}
|
||||
else if (value instanceof Double d) {
|
||||
setDouble(obj, d.doubleValue());
|
||||
}
|
||||
else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(d);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(d);
|
||||
} else {
|
||||
setter.invokeExact(obj, d);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
abstract class MethodHandleFieldAccessorImpl extends FieldAccessorImpl {
|
||||
private static final int IS_READ_ONLY_BIT = 0x0001;
|
||||
private static final int IS_STATIC_BIT = 0x0002;
|
||||
private static final int NONZERO_BIT = 0x8000;
|
||||
|
||||
private final int fieldFlags;
|
||||
protected final MethodHandle getter;
|
||||
protected final MethodHandle setter;
|
||||
|
||||
protected MethodHandleFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field);
|
||||
this.fieldFlags = (isReadOnly ? IS_READ_ONLY_BIT : 0) |
|
||||
(isStatic ? IS_STATIC_BIT : 0) |
|
||||
NONZERO_BIT;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
protected final boolean isReadOnly() {
|
||||
return (fieldFlags & IS_READ_ONLY_BIT) == IS_READ_ONLY_BIT;
|
||||
}
|
||||
|
||||
protected final boolean isStatic() {
|
||||
return (fieldFlags & IS_STATIC_BIT) == IS_STATIC_BIT;
|
||||
}
|
||||
|
||||
protected final void ensureObj(Object o) {
|
||||
if (!isStatic()) {
|
||||
// for compatibility, check the receiver object first
|
||||
// throw NullPointerException if o is null
|
||||
if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
|
||||
throwSetIllegalArgumentException(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IllegalArgumentException because Field::get on the specified object, which
|
||||
* is not an instance of the class or interface declaring the underlying method
|
||||
*/
|
||||
protected IllegalArgumentException newGetIllegalArgumentException(Class<?> type) {
|
||||
return new IllegalArgumentException(getMessage(true, type.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* IllegalArgumentException because Field::set on the specified object, which
|
||||
* is not an instance of the class or interface declaring the underlying method
|
||||
*/
|
||||
protected IllegalArgumentException newSetIllegalArgumentException(Class<?> type) {
|
||||
return new IllegalArgumentException(getMessage(false, type.getName()));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleFloatFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(float.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, float.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(float.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, float.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleFloatFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleFloatFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Float.valueOf(getFloat(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
throw newGetIntIllegalArgumentException();
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
throw newGetLongIllegalArgumentException();
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (float) getter.invokeExact();
|
||||
} else {
|
||||
return (float) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getFloat(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setFloat(obj, b.byteValue());
|
||||
}
|
||||
else if (value instanceof Short s) {
|
||||
setFloat(obj, s.shortValue());
|
||||
}
|
||||
else if (value instanceof Character c) {
|
||||
setFloat(obj, c.charValue());
|
||||
}
|
||||
else if (value instanceof Integer i) {
|
||||
setFloat(obj, i.intValue());
|
||||
}
|
||||
else if (value instanceof Long l) {
|
||||
setFloat(obj, l.longValue());
|
||||
}
|
||||
else if (value instanceof Float f) {
|
||||
setFloat(obj, f.floatValue());
|
||||
}
|
||||
else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setFloat(obj, b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setFloat(obj, c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setFloat(obj, s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setFloat(obj, i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setFloat(obj, l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(f);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(f);
|
||||
} else {
|
||||
setter.invokeExact(obj, f);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleIntegerFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(int.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, int.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(int.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, int.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleIntegerFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleIntegerFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Integer.valueOf(getInt(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (int) getter.invokeExact();
|
||||
} else {
|
||||
return (int) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
return getInt(obj);
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
return getInt(obj);
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getInt(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setInt(obj, b.byteValue());
|
||||
}
|
||||
else if (value instanceof Short s) {
|
||||
setInt(obj, s.shortValue());
|
||||
}
|
||||
else if (value instanceof Character c) {
|
||||
setInt(obj, c.charValue());
|
||||
}
|
||||
else if (value instanceof Integer i) {
|
||||
setInt(obj, i.intValue());
|
||||
}
|
||||
else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setInt(obj, b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setInt(obj, c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setInt(obj, s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(i);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(i);
|
||||
} else {
|
||||
setter.invokeExact(obj, i);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleLongFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(long.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, long.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(long.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, long.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleLongFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleLongFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Long.valueOf(getLong(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
throw newGetIntIllegalArgumentException();
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (long) getter.invokeExact();
|
||||
} else {
|
||||
return (long) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
return getLong(obj);
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getLong(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setLong(obj, b.byteValue());
|
||||
}
|
||||
else if (value instanceof Short s) {
|
||||
setLong(obj, s.shortValue());
|
||||
}
|
||||
else if (value instanceof Character c) {
|
||||
setLong(obj, c.charValue());
|
||||
}
|
||||
else if (value instanceof Integer i) {
|
||||
setLong(obj, i.intValue());
|
||||
}
|
||||
else if (value instanceof Long l) {
|
||||
setLong(obj, l.longValue());
|
||||
}
|
||||
else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setLong(obj, b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setLong(obj, c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setLong(obj, s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setLong(obj, i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(l);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(l);
|
||||
} else {
|
||||
setter.invokeExact(obj, l);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleObjectFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(Object.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, Object.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleObjectFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleObjectFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
return isStatic() ? getter.invokeExact() : getter.invokeExact(obj);
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
throw newGetIntIllegalArgumentException();
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
throw newGetLongIllegalArgumentException();
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
throw newGetFloatIllegalArgumentException();
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
throw newGetDoubleIllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object obj, Object value) throws IllegalAccessException {
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(value);
|
||||
} else {
|
||||
setter.invokeExact(obj, value);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleShortFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(short.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, short.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(short.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, short.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleShortFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleShortFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Short.valueOf(getShort(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (short) getter.invokeExact();
|
||||
} else {
|
||||
return (short) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
return getShort(obj);
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
return getShort(obj);
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
return getShort(obj);
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getShort(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setShort(obj, b.byteValue());
|
||||
}
|
||||
else if (value instanceof Short s) {
|
||||
setShort(obj, s.shortValue());
|
||||
}
|
||||
else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setShort(obj, b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(s);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(s);
|
||||
} else {
|
||||
setter.invokeExact(obj, s);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ class NativeConstructorAccessorImpl extends ConstructorAccessorImpl {
|
|||
IllegalArgumentException,
|
||||
InvocationTargetException
|
||||
{
|
||||
// We can't inflate a constructor belonging to a vm-anonymous class
|
||||
// We can't inflate a constructor belonging to a hidden class
|
||||
// because that kind of class can't be referred to by name, hence can't
|
||||
// be found from the generated bytecode.
|
||||
if (++numInvocations > ReflectionFactory.inflationThreshold()
|
||||
|
|
|
@ -49,7 +49,7 @@ class NativeMethodAccessorImpl extends MethodAccessorImpl {
|
|||
public Object invoke(Object obj, Object[] args)
|
||||
throws IllegalArgumentException, InvocationTargetException
|
||||
{
|
||||
// We can't inflate methods belonging to vm-anonymous classes because
|
||||
// We can't inflate methods belonging to hidden classes because
|
||||
// that kind of class can't be referred to by name, hence can't be
|
||||
// found from the generated bytecode.
|
||||
if (++numInvocations > ReflectionFactory.inflationThreshold()
|
||||
|
|
|
@ -85,6 +85,15 @@ public class ReflectionFactory {
|
|||
private static boolean noInflation = false;
|
||||
private static int inflationThreshold = 15;
|
||||
|
||||
//
|
||||
// New implementation uses direct invocation of method handles
|
||||
private static final int METHOD_MH_ACCESSOR = 0x1;
|
||||
private static final int FIELD_MH_ACCESSOR = 0x2;
|
||||
private static final int ALL_MH_ACCESSORS = METHOD_MH_ACCESSOR|FIELD_MH_ACCESSOR;
|
||||
|
||||
private static int useDirectMethodHandle = ALL_MH_ACCESSORS;
|
||||
private static boolean useNativeAccessorOnly = false; // for testing only
|
||||
|
||||
// true if deserialization constructor checking is disabled
|
||||
private static boolean disableSerialConstructorChecks = false;
|
||||
|
||||
|
@ -137,24 +146,6 @@ public class ReflectionFactory {
|
|||
return soleInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an alternate reflective Method instance for the given method
|
||||
* intended for reflection to invoke, if present.
|
||||
*
|
||||
* A trusted method can define an alternate implementation for a method `foo`
|
||||
* by defining a method named "reflected$foo" that will be invoked
|
||||
* reflectively.
|
||||
*/
|
||||
private static Method findMethodForReflection(Method method) {
|
||||
String altName = "reflected$" + method.getName();
|
||||
try {
|
||||
return method.getDeclaringClass()
|
||||
.getDeclaredMethod(altName, method.getParameterTypes());
|
||||
} catch (NoSuchMethodException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Routines used by java.lang.reflect
|
||||
|
@ -181,39 +172,48 @@ public class ReflectionFactory {
|
|||
}
|
||||
boolean isFinal = Modifier.isFinal(field.getModifiers());
|
||||
boolean isReadOnly = isFinal && (!override || langReflectAccess.isTrustedFinalField(field));
|
||||
return UnsafeFieldAccessorFactory.newFieldAccessor(field, isReadOnly);
|
||||
if (useFieldHandleAccessor()) {
|
||||
return MethodHandleAccessorFactory.newFieldAccessor(field, isReadOnly);
|
||||
} else {
|
||||
return UnsafeFieldAccessorFactory.newFieldAccessor(field, isReadOnly);
|
||||
}
|
||||
}
|
||||
|
||||
public MethodAccessor newMethodAccessor(Method method) {
|
||||
public MethodAccessor newMethodAccessor(Method method, boolean callerSensitive) {
|
||||
checkInitted();
|
||||
|
||||
if (Reflection.isCallerSensitive(method)) {
|
||||
Method altMethod = findMethodForReflection(method);
|
||||
if (altMethod != null) {
|
||||
method = altMethod;
|
||||
}
|
||||
}
|
||||
|
||||
// use the root Method that will not cache caller class
|
||||
Method root = langReflectAccess.getRoot(method);
|
||||
if (root != null) {
|
||||
method = root;
|
||||
}
|
||||
|
||||
if (noInflation && !method.getDeclaringClass().isHidden()) {
|
||||
return new MethodAccessorGenerator().
|
||||
generateMethod(method.getDeclaringClass(),
|
||||
method.getName(),
|
||||
method.getParameterTypes(),
|
||||
method.getReturnType(),
|
||||
method.getExceptionTypes(),
|
||||
method.getModifiers());
|
||||
if (useMethodHandleAccessor()) {
|
||||
return MethodHandleAccessorFactory.newMethodAccessor(method, callerSensitive);
|
||||
} else {
|
||||
NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
|
||||
return acc.getParent();
|
||||
if (noInflation && !method.getDeclaringClass().isHidden()) {
|
||||
return generateMethodAccessor(method);
|
||||
} else {
|
||||
NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
|
||||
return acc.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the MethodAccessor that invokes the given method with
|
||||
* bytecode invocation.
|
||||
*/
|
||||
static MethodAccessorImpl generateMethodAccessor(Method method) {
|
||||
return (MethodAccessorImpl)new MethodAccessorGenerator()
|
||||
.generateMethod(method.getDeclaringClass(),
|
||||
method.getName(),
|
||||
method.getParameterTypes(),
|
||||
method.getReturnType(),
|
||||
method.getExceptionTypes(),
|
||||
method.getModifiers());
|
||||
}
|
||||
|
||||
public ConstructorAccessor newConstructorAccessor(Constructor<?> c) {
|
||||
checkInitted();
|
||||
|
||||
|
@ -232,23 +232,26 @@ public class ReflectionFactory {
|
|||
c = root;
|
||||
}
|
||||
|
||||
// Bootstrapping issue: since we use Class.newInstance() in
|
||||
// the ConstructorAccessor generation process, we have to
|
||||
// break the cycle here.
|
||||
if (Reflection.isSubclassOf(declaringClass,
|
||||
ConstructorAccessorImpl.class)) {
|
||||
return new BootstrapConstructorAccessorImpl(c);
|
||||
}
|
||||
|
||||
if (noInflation && !c.getDeclaringClass().isHidden()) {
|
||||
return new MethodAccessorGenerator().
|
||||
generateConstructor(c.getDeclaringClass(),
|
||||
c.getParameterTypes(),
|
||||
c.getExceptionTypes(),
|
||||
c.getModifiers());
|
||||
if (useMethodHandleAccessor()) {
|
||||
return MethodHandleAccessorFactory.newConstructorAccessor(c);
|
||||
} else {
|
||||
NativeConstructorAccessorImpl acc = new NativeConstructorAccessorImpl(c);
|
||||
return acc.getParent();
|
||||
// Bootstrapping issue: since we use Class.newInstance() in
|
||||
// the ConstructorAccessor generation process, we have to
|
||||
// break the cycle here.
|
||||
if (Reflection.isSubclassOf(declaringClass, ConstructorAccessorImpl.class)) {
|
||||
return new BootstrapConstructorAccessorImpl(c);
|
||||
}
|
||||
|
||||
if (noInflation && !c.getDeclaringClass().isHidden()) {
|
||||
return new MethodAccessorGenerator().
|
||||
generateConstructor(c.getDeclaringClass(),
|
||||
c.getParameterTypes(),
|
||||
c.getExceptionTypes(),
|
||||
c.getModifiers());
|
||||
} else {
|
||||
NativeConstructorAccessorImpl acc = new NativeConstructorAccessorImpl(c);
|
||||
return acc.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -624,6 +627,22 @@ public class ReflectionFactory {
|
|||
return inflationThreshold;
|
||||
}
|
||||
|
||||
static boolean noInflation() {
|
||||
return noInflation;
|
||||
}
|
||||
|
||||
static boolean useMethodHandleAccessor() {
|
||||
return (useDirectMethodHandle & METHOD_MH_ACCESSOR) == METHOD_MH_ACCESSOR;
|
||||
}
|
||||
|
||||
static boolean useFieldHandleAccessor() {
|
||||
return (useDirectMethodHandle & FIELD_MH_ACCESSOR) == FIELD_MH_ACCESSOR;
|
||||
}
|
||||
|
||||
static boolean useNativeAccessorOnly() {
|
||||
return useNativeAccessorOnly;
|
||||
}
|
||||
|
||||
/** We have to defer full initialization of this class until after
|
||||
the static initializer is run since java.lang.reflect.Method's
|
||||
static initializer (more properly, that for
|
||||
|
@ -653,6 +672,20 @@ public class ReflectionFactory {
|
|||
throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e);
|
||||
}
|
||||
}
|
||||
val = props.getProperty("jdk.reflect.useDirectMethodHandle");
|
||||
if (val != null) {
|
||||
if (val.equals("false")) {
|
||||
useDirectMethodHandle = 0;
|
||||
} else if (val.equals("methods")) {
|
||||
useDirectMethodHandle = METHOD_MH_ACCESSOR;
|
||||
} else if (val.equals("fields")) {
|
||||
useDirectMethodHandle = FIELD_MH_ACCESSOR;
|
||||
}
|
||||
}
|
||||
val = props.getProperty("jdk.reflect.useNativeAccessorOnly");
|
||||
if (val != null && val.equals("true")) {
|
||||
useNativeAccessorOnly = true;
|
||||
}
|
||||
|
||||
disableSerialConstructorChecks =
|
||||
"true".equals(props.getProperty("jdk.disableSerialConstructorChecks"));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2021, 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
|
||||
|
@ -28,7 +28,6 @@ package jdk.internal.reflect;
|
|||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
/** Base class for jdk.internal.misc.Unsafe-based FieldAccessors. The
|
||||
observation is that there are only nine types of fields from the
|
||||
|
@ -40,168 +39,16 @@ import jdk.internal.vm.annotation.Stable;
|
|||
abstract class UnsafeFieldAccessorImpl extends FieldAccessorImpl {
|
||||
static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
@Stable
|
||||
protected final long fieldOffset;
|
||||
protected final boolean isFinal;
|
||||
|
||||
UnsafeFieldAccessorImpl(Field field) {
|
||||
super(field);
|
||||
if (Modifier.isStatic(field.getModifiers()))
|
||||
int mods = field.getModifiers();
|
||||
this.isFinal = Modifier.isFinal(mods);
|
||||
if (Modifier.isStatic(mods))
|
||||
fieldOffset = unsafe.staticFieldOffset(field);
|
||||
else
|
||||
fieldOffset = unsafe.objectFieldOffset(field);
|
||||
isFinal = Modifier.isFinal(field.getModifiers());
|
||||
}
|
||||
|
||||
protected void ensureObj(Object o) {
|
||||
// NOTE: will throw NullPointerException, as specified, if o is null
|
||||
if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
|
||||
throwSetIllegalArgumentException(o);
|
||||
}
|
||||
}
|
||||
|
||||
private String getQualifiedFieldName() {
|
||||
return field.getDeclaringClass().getName() + "." +field.getName();
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetIllegalArgumentException(String type) {
|
||||
return new IllegalArgumentException(
|
||||
"Attempt to get "+field.getType().getName()+" field \"" +
|
||||
getQualifiedFieldName() + "\" with illegal data type conversion to "+type
|
||||
);
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(String attemptedType,
|
||||
String attemptedValue)
|
||||
throws IllegalAccessException {
|
||||
throw new IllegalAccessException(getSetMessage(attemptedType, attemptedValue));
|
||||
|
||||
}
|
||||
protected void throwFinalFieldIllegalAccessException(Object o) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException(o != null ? o.getClass().getName() : "", "");
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(boolean z) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("boolean", Boolean.toString(z));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(char b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("char", Character.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(byte b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("byte", Byte.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(short b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("short", Short.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(int i) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("int", Integer.toString(i));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(long i) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("long", Long.toString(i));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(float f) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("float", Float.toString(f));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(double f) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("double", Double.toString(f));
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetBooleanIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("boolean");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetByteIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("byte");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetCharIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("char");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetShortIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("short");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetIntIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("int");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetLongIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("long");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetFloatIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("float");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetDoubleIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("double");
|
||||
}
|
||||
|
||||
protected String getSetMessage(String attemptedType, String attemptedValue) {
|
||||
String err = "Can not set";
|
||||
if (Modifier.isStatic(field.getModifiers()))
|
||||
err += " static";
|
||||
if (isFinal)
|
||||
err += " final";
|
||||
err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " to ";
|
||||
if (!attemptedValue.isEmpty()) {
|
||||
err += "(" + attemptedType + ")" + attemptedValue;
|
||||
} else {
|
||||
if (!attemptedType.isEmpty())
|
||||
err += attemptedType;
|
||||
else
|
||||
err += "null value";
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(String attemptedType,
|
||||
String attemptedValue) {
|
||||
throw new IllegalArgumentException(getSetMessage(attemptedType,attemptedValue));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(Object o) {
|
||||
throwSetIllegalArgumentException(o != null ? o.getClass().getName() : "", "");
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(boolean b) {
|
||||
throwSetIllegalArgumentException("boolean", Boolean.toString(b));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(byte b) {
|
||||
throwSetIllegalArgumentException("byte", Byte.toString(b));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(char c) {
|
||||
throwSetIllegalArgumentException("char", Character.toString(c));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(short s) {
|
||||
throwSetIllegalArgumentException("short", Short.toString(s));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(int i) {
|
||||
throwSetIllegalArgumentException("int", Integer.toString(i));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(long l) {
|
||||
throwSetIllegalArgumentException("long", Long.toString(l));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(float f) {
|
||||
throwSetIllegalArgumentException("float", Float.toString(f));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(double d) {
|
||||
throwSetIllegalArgumentException("double", Double.toString(d));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2021, 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
|
||||
|
@ -26,13 +26,8 @@
|
|||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.security.AccessController;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
/** Base class for jdk.internal.misc.Unsafe-based FieldAccessors for static
|
||||
fields. The observation is that there are only nine types of
|
||||
fields from the standpoint of reflection code: the eight primitive
|
||||
|
@ -46,7 +41,6 @@ abstract class UnsafeStaticFieldAccessorImpl extends UnsafeFieldAccessorImpl {
|
|||
Set.of("base"));
|
||||
}
|
||||
|
||||
@Stable
|
||||
protected final Object base; // base
|
||||
|
||||
UnsafeStaticFieldAccessorImpl(Field field) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue