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

8013527: calling MethodHandles.lookup on itself leads to errors

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

View file

@ -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

View file

@ -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));
}
/**

View file

@ -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);
}
}
}

View file

@ -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");

View file

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

View file

@ -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);
}
}

View file

@ -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);
}
/**

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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;

View file

@ -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;
}
/**

View file

@ -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"
);
}

View file

@ -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 {
}

View file

@ -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);
}
}

View file

@ -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

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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));
}
}

View file

@ -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;
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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();
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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()));
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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()

View file

@ -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()

View file

@ -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"));

View file

@ -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));
}
}

View file

@ -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) {