8271820: Implementation of JEP 416: Reimplement Core Reflection with Method Handle
8013527: calling MethodHandles.lookup on itself leads to errors Co-authored-by: Peter Levart <plevart@openjdk.org> Co-authored-by: Claes Redestad <redestad@openjdk.org> Co-authored-by: Mandy Chung <mchung@openjdk.org> Reviewed-by: mcimadamore, plevart, egahlin, redestad, cjplummer, alanb
This commit is contained in:
parent
5a768f75c9
commit
c6339cb8a2
@ -116,6 +116,12 @@ public class HelloClasslist {
|
||||
DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.ROOT)
|
||||
.format(new Date()));
|
||||
|
||||
// A selection of trivial and common reflection operations
|
||||
var instance = HelloClasslist.class.getConstructor().newInstance();
|
||||
HelloClasslist.class.getMethod("staticMethod_V").invoke(null);
|
||||
var obj = HelloClasslist.class.getMethod("staticMethod_L_L", Object.class).invoke(null, instance);
|
||||
HelloClasslist.class.getField("field").get(instance);
|
||||
|
||||
// A selection of trivial and relatively common MH operations
|
||||
invoke(MethodHandles.identity(double.class), 1.0);
|
||||
invoke(MethodHandles.identity(int.class), 1);
|
||||
@ -126,8 +132,14 @@ public class HelloClasslist {
|
||||
LOGGER.log(Level.FINE, "New Date: " + newDate + " - old: " + oldDate);
|
||||
}
|
||||
|
||||
public HelloClasslist() {}
|
||||
|
||||
public String field = "someValue";
|
||||
|
||||
public static void staticMethod_V() {}
|
||||
|
||||
public static Object staticMethod_L_L(Object o) { return o; }
|
||||
|
||||
private static MethodHandle handle(String name, MethodType type) throws Throwable {
|
||||
return MethodHandles.lookup().findStatic(HelloClasslist.class, name, type);
|
||||
}
|
||||
|
@ -224,6 +224,7 @@ static bool trust_final_non_static_fields(ciInstanceKlass* holder) {
|
||||
return false;
|
||||
// Even if general trusting is disabled, trust system-built closures in these packages.
|
||||
if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke") ||
|
||||
holder->is_in_package("java/lang/reflect") || holder->is_in_package("jdk/internal/reflect") ||
|
||||
holder->is_in_package("jdk/internal/foreign") || holder->is_in_package("jdk/incubator/foreign") ||
|
||||
holder->is_in_package("jdk/internal/vm/vector") || holder->is_in_package("jdk/incubator/vector") ||
|
||||
holder->is_in_package("java/lang"))
|
||||
|
@ -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,6 +463,20 @@ 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) {
|
||||
@ -523,13 +544,24 @@ public final class Class<T> implements java.io.Serializable,
|
||||
@SuppressWarnings("removal")
|
||||
@CallerSensitive
|
||||
public static Class<?> forName(Module module, String name) {
|
||||
Class<?> caller = null;
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
caller = Reflection.getCallerClass();
|
||||
}
|
||||
return forName(module, name, caller);
|
||||
}
|
||||
|
||||
// Caller-sensitive adapter method for reflective invocation
|
||||
@SuppressWarnings("removal")
|
||||
@CallerSensitiveAdapter
|
||||
private static Class<?> forName(Module module, String name, Class<?> caller) {
|
||||
Objects.requireNonNull(module);
|
||||
Objects.requireNonNull(name);
|
||||
|
||||
ClassLoader cl;
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
Class<?> caller = Reflection.getCallerClass();
|
||||
if (caller != null && caller.getModule() != module) {
|
||||
// if caller is null, Class.forName is the last java frame on the stack.
|
||||
// java.base has all permissions
|
||||
|
@ -64,6 +64,7 @@ import jdk.internal.perf.PerfCounter;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.CallerSensitiveAdapter;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.util.StaticProperty;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
@ -1615,9 +1616,13 @@ public abstract class ClassLoader {
|
||||
*/
|
||||
@CallerSensitive
|
||||
protected static boolean registerAsParallelCapable() {
|
||||
Class<? extends ClassLoader> callerClass =
|
||||
Reflection.getCallerClass().asSubclass(ClassLoader.class);
|
||||
return ParallelLoaders.register(callerClass);
|
||||
return registerAsParallelCapable(Reflection.getCallerClass());
|
||||
}
|
||||
|
||||
// Caller-sensitive adapter method for reflective invocation
|
||||
@CallerSensitiveAdapter
|
||||
private static boolean registerAsParallelCapable(Class<?> caller) {
|
||||
return ParallelLoaders.register(caller.asSubclass(ClassLoader.class));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,6 @@
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.misc.CDS;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import sun.invoke.util.BytecodeDescriptor;
|
||||
import sun.invoke.util.VerifyAccess;
|
||||
@ -37,7 +36,6 @@ import java.io.FilePermission;
|
||||
import java.io.Serializable;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
@ -46,8 +44,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.PropertyPermission;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
@ -57,7 +57,6 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
* @see LambdaMetafactory
|
||||
*/
|
||||
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
|
||||
private static final int CLASSFILE_VERSION = VM.classFileVersion();
|
||||
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
|
||||
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
|
||||
private static final String NAME_CTOR = "<init>";
|
||||
@ -106,7 +105,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
|
||||
|
||||
// condy to load implMethod from class data
|
||||
MethodType classDataMType = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
|
||||
MethodType classDataMType = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
|
||||
Handle classDataBsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData",
|
||||
classDataMType.descriptorString(), false);
|
||||
implMethodCondy = new ConstantDynamic(ConstantDescs.DEFAULT_NAME, MethodHandle.class.descriptorString(), classDataBsm);
|
||||
@ -227,10 +226,7 @@ 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) {
|
||||
if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
|
||||
try {
|
||||
return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD,
|
||||
factoryType.returnType()));
|
||||
@ -238,39 +234,20 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
MethodHandle mh = caller.findConstructor(innerClass, constructorType);
|
||||
if (factoryType.parameterCount() == 0) {
|
||||
// In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance
|
||||
Object inst = mh.asType(methodType(Object.class)).invokeExact();
|
||||
return new ConstantCallSite(MethodHandles.constant(interfaceClass, inst));
|
||||
} else {
|
||||
return new ConstantCallSite(mh.asType(factoryType));
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new LambdaConversionException("Exception finding constructor", e);
|
||||
} catch (Throwable e) {
|
||||
throw new LambdaConversionException("Exception instantiating lambda object", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.BasicType;
|
||||
@ -316,7 +317,7 @@ class InvokerBytecodeGenerator {
|
||||
* Extract the MemberName of a newly-defined method.
|
||||
*/
|
||||
private MemberName loadMethod(byte[] classFile) {
|
||||
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile)
|
||||
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile, Set.of())
|
||||
.defineClass(true, classDataValues());
|
||||
return resolveInvokerMember(invokerClass, invokerName, invokerType);
|
||||
}
|
||||
@ -376,7 +377,7 @@ class InvokerBytecodeGenerator {
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(Type.getType("L" + className + ";"));
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandleNatives",
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandles",
|
||||
"classData", "(Ljava/lang/Class;)Ljava/lang/Object;", false);
|
||||
// we should optimize one single element case that does not need to create a List
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/util/List");
|
||||
|
@ -42,6 +42,8 @@ import sun.invoke.util.Wrapper;
|
||||
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -50,6 +52,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
@ -57,6 +60,7 @@ import java.util.stream.Stream;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
@ -1030,6 +1034,7 @@ abstract class MethodHandleImpl {
|
||||
// That way we can lazily load the code and set up the constants.
|
||||
private static class BindCaller {
|
||||
private static MethodType INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
|
||||
private static MethodType REFLECT_INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object.class, Object[].class);
|
||||
|
||||
static MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
|
||||
// Code in the boot layer should now be careful while creating method handles or
|
||||
@ -1042,15 +1047,48 @@ abstract class MethodHandleImpl {
|
||||
hostClass.getName().startsWith("java.lang.invoke."))) {
|
||||
throw new InternalError(); // does not happen, and should not anyway
|
||||
}
|
||||
|
||||
MemberName member = mh.internalMemberName();
|
||||
if (member != null) {
|
||||
// Look up the CSM adapter method with the same method name
|
||||
// but with an additional caller class parameter. If present,
|
||||
// bind the adapter's method handle with the lookup class as
|
||||
// the caller class argument
|
||||
MemberName csmAdapter = IMPL_LOOKUP.resolveOrNull(member.getReferenceKind(),
|
||||
new MemberName(member.getDeclaringClass(),
|
||||
member.getName(),
|
||||
member.getMethodType().appendParameterTypes(Class.class),
|
||||
member.getReferenceKind()));
|
||||
if (csmAdapter != null) {
|
||||
assert !csmAdapter.isCallerSensitive();
|
||||
MethodHandle dmh = DirectMethodHandle.make(csmAdapter);
|
||||
dmh = MethodHandles.insertArguments(dmh, dmh.type().parameterCount() - 1, hostClass);
|
||||
dmh = new WrappedMember(dmh, mh.type(), member, mh.isInvokeSpecial(), hostClass);
|
||||
return dmh;
|
||||
}
|
||||
}
|
||||
|
||||
// If no adapter method for CSM with an additional Class parameter
|
||||
// is present, then inject an invoker class that is the caller
|
||||
// invoking the method handle of the CSM
|
||||
try {
|
||||
return bindCallerWithInjectedInvoker(mh, hostClass);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodHandle bindCallerWithInjectedInvoker(MethodHandle mh, Class<?> hostClass)
|
||||
throws ReflectiveOperationException
|
||||
{
|
||||
// For simplicity, convert mh to a varargs-like method.
|
||||
MethodHandle vamh = prepareForInvoker(mh);
|
||||
// Cache the result of makeInjectedInvoker once per argument class.
|
||||
MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass);
|
||||
MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass).invoker();
|
||||
return restoreToType(bccInvoker.bindTo(vamh), mh, hostClass);
|
||||
}
|
||||
|
||||
private static MethodHandle makeInjectedInvoker(Class<?> targetClass) {
|
||||
try {
|
||||
private static Class<?> makeInjectedInvoker(Class<?> targetClass) {
|
||||
/*
|
||||
* The invoker class defined to the same class loader as the lookup class
|
||||
* but in an unnamed package so that the class bytes can be cached and
|
||||
@ -1064,21 +1102,80 @@ abstract class MethodHandleImpl {
|
||||
name = name.replace('/', '_');
|
||||
}
|
||||
Class<?> invokerClass = new Lookup(targetClass)
|
||||
.makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE)
|
||||
.defineClass(true);
|
||||
.makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE, Set.of(NESTMATE))
|
||||
.defineClass(true, targetClass);
|
||||
assert checkInjectedInvoker(targetClass, invokerClass);
|
||||
return IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
return invokerClass;
|
||||
}
|
||||
|
||||
private static ClassValue<MethodHandle> CV_makeInjectedInvoker = new ClassValue<MethodHandle>() {
|
||||
@Override protected MethodHandle computeValue(Class<?> hostClass) {
|
||||
return makeInjectedInvoker(hostClass);
|
||||
private static ClassValue<InjectedInvokerHolder> CV_makeInjectedInvoker = new ClassValue<>() {
|
||||
@Override
|
||||
protected InjectedInvokerHolder computeValue(Class<?> hostClass) {
|
||||
return new InjectedInvokerHolder(makeInjectedInvoker(hostClass));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns a method handle of an invoker class injected for reflection
|
||||
* implementation use with the following signature:
|
||||
* reflect_invoke_V(MethodHandle mh, Object target, Object[] args)
|
||||
*
|
||||
* Method::invoke on a caller-sensitive method will call
|
||||
* MethodAccessorImpl::invoke(Object, Object[]) through reflect_invoke_V
|
||||
* target.csm(args)
|
||||
* NativeMethodAccesssorImpl::invoke(target, args)
|
||||
* MethodAccessImpl::invoke(target, args)
|
||||
* InjectedInvoker::reflect_invoke_V(vamh, target, args);
|
||||
* method::invoke(target, args)
|
||||
* p.Foo::m
|
||||
*
|
||||
* An injected invoker class is a hidden class which has the same
|
||||
* defining class loader, runtime package, and protection domain
|
||||
* as the given caller class.
|
||||
*/
|
||||
static MethodHandle reflectiveInvoker(Class<?> caller) {
|
||||
return BindCaller.CV_makeInjectedInvoker.get(caller).reflectInvoker();
|
||||
}
|
||||
|
||||
private static final class InjectedInvokerHolder {
|
||||
private final Class<?> invokerClass;
|
||||
// lazily resolved and cached DMH(s) of invoke_V methods
|
||||
private MethodHandle invoker;
|
||||
private MethodHandle reflectInvoker;
|
||||
|
||||
private InjectedInvokerHolder(Class<?> invokerClass) {
|
||||
this.invokerClass = invokerClass;
|
||||
}
|
||||
|
||||
private MethodHandle invoker() {
|
||||
var mh = invoker;
|
||||
if (mh == null) {
|
||||
try {
|
||||
invoker = mh = IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
|
||||
} catch (Error | RuntimeException ex) {
|
||||
throw ex;
|
||||
} catch (Throwable ex) {
|
||||
throw new InternalError(ex);
|
||||
}
|
||||
}
|
||||
return mh;
|
||||
}
|
||||
|
||||
private MethodHandle reflectInvoker() {
|
||||
var mh = reflectInvoker;
|
||||
if (mh == null) {
|
||||
try {
|
||||
reflectInvoker = mh = IMPL_LOOKUP.findStatic(invokerClass, "reflect_invoke_V", REFLECT_INVOKER_MT);
|
||||
} catch (Error | RuntimeException ex) {
|
||||
throw ex;
|
||||
} catch (Throwable ex) {
|
||||
throw new InternalError(ex);
|
||||
}
|
||||
}
|
||||
return mh;
|
||||
}
|
||||
}
|
||||
|
||||
// Adapt mh so that it can be called directly from an injected invoker:
|
||||
private static MethodHandle prepareForInvoker(MethodHandle mh) {
|
||||
mh = mh.asFixedArity();
|
||||
@ -1115,6 +1212,8 @@ abstract class MethodHandleImpl {
|
||||
MethodHandle invoker = IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
|
||||
MethodHandle vamh = prepareForInvoker(MH_checkCallerClass);
|
||||
return (boolean)invoker.invoke(vamh, new Object[]{ invokerClass });
|
||||
} catch (Error|RuntimeException ex) {
|
||||
throw ex;
|
||||
} catch (Throwable ex) {
|
||||
throw new InternalError(ex);
|
||||
}
|
||||
@ -1151,14 +1250,21 @@ 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);
|
||||
|
||||
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "invoke_V",
|
||||
{
|
||||
var mv = cw.visitMethod(ACC_STATIC, "invoke_V",
|
||||
"(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
null, null);
|
||||
|
||||
@ -1172,6 +1278,22 @@ abstract class MethodHandleImpl {
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
}
|
||||
|
||||
{
|
||||
var mv = cw.visitMethod(ACC_STATIC, "reflect_invoke_V",
|
||||
"(Ljava/lang/invoke/MethodHandle;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
|
||||
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(3, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
return cw.toByteArray();
|
||||
}
|
||||
}
|
||||
@ -1503,6 +1625,48 @@ abstract class MethodHandleImpl {
|
||||
public VarHandle insertCoordinates(VarHandle target, int pos, Object... values) {
|
||||
return VarHandles.insertCoordinates(target, pos, values);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MethodHandle unreflectConstructor(Constructor<?> ctor) throws IllegalAccessException {
|
||||
return IMPL_LOOKUP.unreflectConstructor(ctor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle unreflectField(Field field, boolean isSetter) throws IllegalAccessException {
|
||||
return isSetter ? IMPL_LOOKUP.unreflectSetter(field) : IMPL_LOOKUP.unreflectGetter(field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws IllegalAccessException {
|
||||
try {
|
||||
return IMPL_LOOKUP.findVirtual(defc, name, type);
|
||||
} catch (NoSuchMethodException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws IllegalAccessException {
|
||||
try {
|
||||
return IMPL_LOOKUP.findStatic(defc, name, type);
|
||||
} catch (NoSuchMethodException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle reflectiveInvoker(Class<?> caller) {
|
||||
Objects.requireNonNull(caller);
|
||||
return BindCaller.reflectiveInvoker(caller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize) {
|
||||
// skip name and access flags validation
|
||||
return caller.makeHiddenClassDefiner(name, bytes, Set.of()).defineClassAsLookup(initialize, classData);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,7 @@
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
@ -35,7 +34,6 @@ import java.lang.reflect.Field;
|
||||
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
|
||||
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
/**
|
||||
@ -248,6 +246,7 @@ class MethodHandleNatives {
|
||||
return true;
|
||||
}
|
||||
static {
|
||||
VM.setJavaLangInvokeInited();
|
||||
assert(verifyConstants());
|
||||
}
|
||||
|
||||
@ -665,8 +664,7 @@ class MethodHandleNatives {
|
||||
|
||||
static boolean canBeCalledVirtual(MemberName mem) {
|
||||
assert(mem.isInvocable());
|
||||
return mem.getName().equals("getContextClassLoader") &&
|
||||
canBeCalledVirtual(mem, java.lang.Thread.class);
|
||||
return mem.getName().equals("getContextClassLoader") && canBeCalledVirtual(mem, java.lang.Thread.class);
|
||||
}
|
||||
|
||||
static boolean canBeCalledVirtual(MemberName symbolicRef, Class<?> definingClass) {
|
||||
@ -676,16 +674,4 @@ class MethodHandleNatives {
|
||||
return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef
|
||||
symbolicRefClass.isInterface()); // Mdef implements Msym
|
||||
}
|
||||
|
||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
/*
|
||||
* Returns the class data set by the VM in the Class::classData field.
|
||||
*
|
||||
* This is also invoked by LambdaForms as it cannot use condy via
|
||||
* MethodHandles.classData due to bootstrapping issue.
|
||||
*/
|
||||
static Object classData(Class<?> c) {
|
||||
UNSAFE.ensureClassInitialized(c);
|
||||
return JLA.classData(c);
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.CallerSensitiveAdapter;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import sun.invoke.util.ValueConversions;
|
||||
@ -63,7 +64,9 @@ import java.util.stream.Stream;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.V_TYPE;
|
||||
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
|
||||
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
|
||||
import static java.lang.invoke.MethodHandleStatics.newInternalError;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
/**
|
||||
@ -117,14 +120,15 @@ public class MethodHandles {
|
||||
}
|
||||
|
||||
/**
|
||||
* This reflected$lookup method is the alternate implementation of
|
||||
* the lookup method when being invoked by reflection.
|
||||
* This lookup method is the alternate implementation of
|
||||
* the lookup method with a leading caller class argument which is
|
||||
* non-caller-sensitive. This method is only invoked by reflection
|
||||
* and method handle.
|
||||
*/
|
||||
@CallerSensitive
|
||||
private static Lookup reflected$lookup() {
|
||||
Class<?> caller = Reflection.getCallerClass();
|
||||
@CallerSensitiveAdapter
|
||||
private static Lookup lookup(Class<?> caller) {
|
||||
if (caller.getClassLoader() == null) {
|
||||
throw newIllegalArgumentException("illegal lookupClass: "+caller);
|
||||
throw newInternalError("calling lookup() reflectively is not supported: "+caller);
|
||||
}
|
||||
return new Lookup(caller);
|
||||
}
|
||||
@ -329,7 +333,7 @@ public class MethodHandles {
|
||||
throw new IllegalAccessException(caller + " does not have ORIGINAL access");
|
||||
}
|
||||
|
||||
Object classdata = MethodHandleNatives.classData(caller.lookupClass());
|
||||
Object classdata = classData(caller.lookupClass());
|
||||
if (classdata == null) return null;
|
||||
|
||||
try {
|
||||
@ -341,6 +345,17 @@ public class MethodHandles {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the class data set by the VM in the Class::classData field.
|
||||
*
|
||||
* This is also invoked by LambdaForms as it cannot use condy via
|
||||
* MethodHandles::classData due to bootstrapping issue.
|
||||
*/
|
||||
static Object classData(Class<?> c) {
|
||||
UNSAFE.ensureClassInitialized(c);
|
||||
return SharedSecrets.getJavaLangAccess().classData(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element at the specified index in the
|
||||
* {@linkplain #classData(Lookup, String, Class) class data},
|
||||
@ -2359,15 +2374,16 @@ public class MethodHandles {
|
||||
|
||||
/**
|
||||
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
|
||||
* from the given bytes. No package name check on the given name.
|
||||
* from the given bytes and the given options. No package name check on the given name.
|
||||
*
|
||||
* @param name fully-qualified name that specifies the prefix of the hidden class
|
||||
* @param bytes class bytes
|
||||
* @return ClassDefiner that defines a hidden class of the given bytes.
|
||||
* @param options class options
|
||||
* @return ClassDefiner that defines a hidden class of the given bytes and options.
|
||||
*/
|
||||
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes) {
|
||||
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, Set<ClassOption> options) {
|
||||
// skip name and access flags validation
|
||||
return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), Set.of(), false);
|
||||
return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), options, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,7 @@
|
||||
package java.lang.reflect;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.ConstructorAccessor;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
@ -63,19 +64,17 @@ import java.util.StringJoiner;
|
||||
* @since 1.1
|
||||
*/
|
||||
public final class Constructor<T> extends Executable {
|
||||
@Stable
|
||||
private Class<T> clazz;
|
||||
private int slot;
|
||||
private Class<?>[] parameterTypes;
|
||||
private Class<?>[] exceptionTypes;
|
||||
@Stable
|
||||
private int modifiers;
|
||||
private final Class<T> clazz;
|
||||
private final int slot;
|
||||
private final Class<?>[] parameterTypes;
|
||||
private final Class<?>[] exceptionTypes;
|
||||
private final int modifiers;
|
||||
// Generics and annotations support
|
||||
private transient String signature;
|
||||
private final transient String signature;
|
||||
// generic info repository; lazily initialized
|
||||
private transient ConstructorRepository genericInfo;
|
||||
private byte[] annotations;
|
||||
private byte[] parameterAnnotations;
|
||||
private final byte[] annotations;
|
||||
private final byte[] parameterAnnotations;
|
||||
|
||||
// Generics infrastructure
|
||||
// Accessor for factory
|
||||
@ -492,9 +491,6 @@ public final class Constructor<T> extends Executable {
|
||||
if (checkAccess)
|
||||
checkAccess(caller, clazz, clazz, modifiers);
|
||||
|
||||
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
|
||||
throw new IllegalArgumentException("Cannot reflectively create enum objects");
|
||||
|
||||
ConstructorAccessor ca = constructorAccessor; // read @Stable
|
||||
if (ca == null) {
|
||||
ca = acquireConstructorAccessor();
|
||||
@ -534,6 +530,7 @@ public final class Constructor<T> extends Executable {
|
||||
// synchronization will probably make the implementation more
|
||||
// scalable.
|
||||
private ConstructorAccessor acquireConstructorAccessor() {
|
||||
|
||||
// First check to see if one has been created yet, and take it
|
||||
// if so.
|
||||
Constructor<?> root = this.root;
|
||||
@ -542,7 +539,13 @@ 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);
|
||||
// set the constructor accessor only if it's not using native implementation
|
||||
if (VM.isJavaLangInvokeInited())
|
||||
setConstructorAccessor(tmp);
|
||||
}
|
||||
|
||||
|
@ -65,23 +65,19 @@ import sun.reflect.annotation.TypeAnnotationParser;
|
||||
*/
|
||||
public final
|
||||
class Field extends AccessibleObject implements Member {
|
||||
|
||||
@Stable
|
||||
private Class<?> clazz;
|
||||
private int slot;
|
||||
private final Class<?> clazz;
|
||||
private final int slot;
|
||||
// This is guaranteed to be interned by the VM in the 1.4
|
||||
// reflection implementation
|
||||
private String name;
|
||||
@Stable
|
||||
private Class<?> type;
|
||||
@Stable
|
||||
private int modifiers;
|
||||
private boolean trustedFinal;
|
||||
private final String name;
|
||||
private final Class<?> type;
|
||||
private final int modifiers;
|
||||
private final boolean trustedFinal;
|
||||
// Generics and annotations support
|
||||
private transient String signature;
|
||||
private final transient String signature;
|
||||
// generic info repository; lazily initialized
|
||||
private transient FieldRepository genericInfo;
|
||||
private byte[] annotations;
|
||||
private final byte[] annotations;
|
||||
// Cached field accessor created without override
|
||||
@Stable
|
||||
private FieldAccessor fieldAccessor;
|
||||
|
@ -26,7 +26,9 @@
|
||||
package java.lang.reflect;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.CallerSensitiveAdapter;
|
||||
import jdk.internal.reflect.MethodAccessor;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
@ -67,24 +69,22 @@ import java.util.StringJoiner;
|
||||
* @since 1.1
|
||||
*/
|
||||
public final class Method extends Executable {
|
||||
@Stable
|
||||
private Class<?> clazz;
|
||||
private int slot;
|
||||
private final Class<?> clazz;
|
||||
private final int slot;
|
||||
// This is guaranteed to be interned by the VM in the 1.4
|
||||
// reflection implementation
|
||||
private String name;
|
||||
private Class<?> returnType;
|
||||
private Class<?>[] parameterTypes;
|
||||
private Class<?>[] exceptionTypes;
|
||||
@Stable
|
||||
private int modifiers;
|
||||
private final String name;
|
||||
private final Class<?> returnType;
|
||||
private final Class<?>[] parameterTypes;
|
||||
private final Class<?>[] exceptionTypes;
|
||||
private final int modifiers;
|
||||
// Generics and annotations support
|
||||
private transient String signature;
|
||||
private final transient String signature;
|
||||
// generic info repository; lazily initialized
|
||||
private transient MethodRepository genericInfo;
|
||||
private byte[] annotations;
|
||||
private byte[] parameterAnnotations;
|
||||
private byte[] annotationDefault;
|
||||
private final byte[] annotations;
|
||||
private final byte[] parameterAnnotations;
|
||||
private final byte[] annotationDefault;
|
||||
@Stable
|
||||
private MethodAccessor methodAccessor;
|
||||
// For sharing of MethodAccessors. This branching structure is
|
||||
@ -553,20 +553,64 @@ public final class Method extends Executable {
|
||||
@ForceInline // to ensure Reflection.getCallerClass optimization
|
||||
@IntrinsicCandidate
|
||||
public Object invoke(Object obj, Object... args)
|
||||
throws IllegalAccessException, IllegalArgumentException,
|
||||
InvocationTargetException
|
||||
throws IllegalAccessException, InvocationTargetException
|
||||
{
|
||||
boolean callerSensitive = isCallerSensitive();
|
||||
Class<?> caller = null;
|
||||
if (!override || callerSensitive) {
|
||||
caller = Reflection.getCallerClass();
|
||||
}
|
||||
|
||||
// Reflection::getCallerClass filters all subclasses of
|
||||
// jdk.internal.reflect.MethodAccessorImpl and Method::invoke(Object, Object[])
|
||||
// Should not call Method::invoke(Object, Object[], Class) here
|
||||
if (!override) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@Stable private Boolean callerSensitive; // lazily initialize
|
||||
private boolean isCallerSensitive() {
|
||||
Boolean cs = callerSensitive;
|
||||
if (cs == null) {
|
||||
callerSensitive = cs = Reflection.isCallerSensitive(this);
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -672,7 +716,9 @@ public final class Method extends Executable {
|
||||
methodAccessor = tmp;
|
||||
} else {
|
||||
// Otherwise fabricate one and propagate it up to the root
|
||||
tmp = reflectionFactory.newMethodAccessor(this);
|
||||
tmp = reflectionFactory.newMethodAccessor(this, isCallerSensitive());
|
||||
// set the method accessor only if it's not using native implementation
|
||||
if (VM.isJavaLangInvokeInited())
|
||||
setMethodAccessor(tmp);
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,11 @@ package jdk.internal.access;
|
||||
import jdk.internal.invoke.NativeEntryPoint;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -139,4 +142,43 @@ public interface JavaLangInvokeAccess {
|
||||
* @param mh the method handle
|
||||
*/
|
||||
void ensureCustomized(MethodHandle mh);
|
||||
|
||||
/**
|
||||
* Produces a method handle unreflecting from a {@code Constructor} with
|
||||
* the trusted lookup
|
||||
*/
|
||||
MethodHandle unreflectConstructor(Constructor<?> ctor) throws IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Produces a method handle unreflecting from a {@code Field} with
|
||||
* the trusted lookup
|
||||
*/
|
||||
MethodHandle unreflectField(Field field, boolean isSetter) throws IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Produces a method handle of a virtual method with the trusted lookup.
|
||||
*/
|
||||
MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Produces a method handle of a static method with the trusted lookup.
|
||||
*/
|
||||
MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Returns a method handle of an invoker class injected for core reflection
|
||||
* implementation with the following signature:
|
||||
* reflect_invoke_V(MethodHandle mh, Object target, Object[] args)
|
||||
*
|
||||
* The invoker class is a hidden class which has the same
|
||||
* defining class loader, runtime package, and protection domain
|
||||
* as the given caller class.
|
||||
*/
|
||||
MethodHandle reflectiveInvoker(Class<?> caller);
|
||||
|
||||
/**
|
||||
* Defines a hidden class of the given name and bytes with class data.
|
||||
* The given bytes is trusted.
|
||||
*/
|
||||
Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize);
|
||||
}
|
||||
|
@ -51,7 +51,6 @@ import java.security.Signature;
|
||||
for this purpose, namely the loss of compile-time checking. */
|
||||
|
||||
public class SharedSecrets {
|
||||
private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
private static JavaAWTAccess javaAWTAccess;
|
||||
private static JavaAWTFontAccess javaAWTFontAccess;
|
||||
private static JavaBeansAccess javaBeansAccess;
|
||||
|
@ -27,14 +27,13 @@ package jdk.internal.misc;
|
||||
|
||||
import static java.lang.Thread.State.*;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
import sun.nio.ch.FileChannelImpl;
|
||||
|
||||
public class VM {
|
||||
@ -91,7 +90,19 @@ public class VM {
|
||||
* @see java.lang.System#initPhase2
|
||||
*/
|
||||
public static boolean isModuleSystemInited() {
|
||||
return VM.initLevel() >= MODULE_SYSTEM_INITED;
|
||||
return initLevel >= MODULE_SYSTEM_INITED;
|
||||
}
|
||||
|
||||
private static @Stable boolean javaLangInvokeInited;
|
||||
public static void setJavaLangInvokeInited() {
|
||||
if (javaLangInvokeInited) {
|
||||
throw new InternalError("java.lang.invoke already inited");
|
||||
}
|
||||
javaLangInvokeInited = true;
|
||||
}
|
||||
|
||||
public static boolean isJavaLangInvokeInited() {
|
||||
return javaLangInvokeInited;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Utility methods used by DirectMethodHandleAccessor and DirectConstructorHandleAccessor
|
||||
*/
|
||||
public class AccessorUtils {
|
||||
/**
|
||||
* Determines if the given exception thrown by MethodHandle::invokeExact
|
||||
* is caused by an illegal argument passed to Method::invoke or
|
||||
* Constructor::newInstance. This method inspects the stack trace of
|
||||
* the exception to detect if it is thrown by the method handle core
|
||||
* implementation or the implementation of the reflected method or constructor.
|
||||
*
|
||||
* MethodHandle::invoke throws ClassCastException if the receiver object
|
||||
* is not an instance of the declaring class of the method if the method
|
||||
* is an instance method, or if a parameter value cannot be converted
|
||||
* to the corresponding formal parameter type. It throws
|
||||
* NullPointerException if the receiver object is null if the method
|
||||
* is an instance method, or if unboxing operation of a parameter fails
|
||||
* because the parameter value is null. It throws WrongMethodTypeException
|
||||
* if the method type mismatches.
|
||||
*
|
||||
* @param accessorType the accessor class that does the method handle invocation
|
||||
* @param e ClassCastException, NullPointerException or WrongMethodTypeException
|
||||
*/
|
||||
static boolean isIllegalArgument(Class<?> accessorType, RuntimeException e) {
|
||||
assert(e instanceof ClassCastException || e instanceof NullPointerException ||
|
||||
e instanceof WrongMethodTypeException);
|
||||
|
||||
StackTraceElement[] stackTrace = e.getStackTrace();
|
||||
if (stackTrace.length == 0) {
|
||||
return false; // would this happen?
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
StackTraceElement frame = stackTrace[0];
|
||||
// Class::cast and Objects::requiresNonNull may be thrown by the implementation
|
||||
// of the reflected method/constructor. Skip them and continue.
|
||||
if ((frame.getClassName().equals("java.lang.Class") && frame.getMethodName().equals("cast"))
|
||||
|| (frame.getClassName().equals("java.util.Objects") && frame.getMethodName().equals("requiresNonNull"))) {
|
||||
i++;
|
||||
}
|
||||
for (; i < stackTrace.length; i++) {
|
||||
frame = stackTrace[i];
|
||||
String cname = frame.getClassName();
|
||||
// it's illegal argument if this exception is thrown from accessorType
|
||||
if (cname.equals(accessorType.getName())) {
|
||||
return true;
|
||||
}
|
||||
// if this exception is thrown from an unnamed module or not from java.base
|
||||
// then i.e. not from method handle core implementation
|
||||
if (frame.getModuleName() == null || !frame.getModuleName().equals("java.base")) {
|
||||
return false;
|
||||
}
|
||||
int index = cname.lastIndexOf(".");
|
||||
String pn = index > 0 ? cname.substring(0, index) : "";
|
||||
// exception thrown from java.base but not from core reflection/method handle internals
|
||||
if (!IMPL_PACKAGES.contains(pn)) {
|
||||
return false;
|
||||
}
|
||||
// If Constructor::newInstance is invoked by Method::invoke or vice versa,
|
||||
// so the exception is thrown from the implementation body of the reflected
|
||||
// method or constructor
|
||||
if ((accessorType == DirectMethodHandleAccessor.class
|
||||
&& cname.startsWith(DirectConstructorHandleAccessor.class.getName()))
|
||||
|| (accessorType == DirectConstructorHandleAccessor.class
|
||||
&& cname.startsWith(DirectMethodHandleAccessor.class.getName()))) {
|
||||
// thrown from another reflection accessor impl class
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final Set<String> IMPL_PACKAGES = Set.of(
|
||||
"java.lang.reflect",
|
||||
"java.lang.invoke",
|
||||
"jdk.internal.reflect",
|
||||
"sun.invoke.util"
|
||||
);
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
|
||||
/**
|
||||
* A method annotated @CallerSensitiveAdapter is an adapter method corresponding
|
||||
* to a caller-sensitive method, which is annotated with @CallerSensitive.
|
||||
*
|
||||
* A caller-sensitive adapter is private and has the same name as its
|
||||
* corresponding caller-sensitive method with a trailing caller class parameter.
|
||||
*
|
||||
* When a caller-sensitive method is invoked via Method::invoke or MethodHandle
|
||||
* the core reflection and method handle implementation will find if
|
||||
* an @CallerSensitiveAdapter method for that CSM is present. If present,
|
||||
* the runtime will invoke the adapter method with the caller class
|
||||
* argument instead. This special calling sequence ensures that the same caller
|
||||
* class is passed to a caller-sensitive method via Method::invoke,
|
||||
* MethodHandle::invokeExact, or a mix of these methods.
|
||||
*
|
||||
* For example, CSM::returnCallerClass is a caller-sensitive method
|
||||
* with an adapter method:
|
||||
* {@code
|
||||
* class CSM {
|
||||
* @CallerSensitive
|
||||
* static Class<?> returnCallerClass() {
|
||||
* return returnCallerClass(Reflection.getCallerClass());
|
||||
* }
|
||||
* @CallerSensitiveAdapter
|
||||
* private static Class<?> returnCallerClass(Class<?> caller) {
|
||||
* return caller;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* class Test {
|
||||
* void test() throws Throwable {
|
||||
* // calling CSM::returnCallerClass via reflection
|
||||
* var csm = CSM.class.getMethod("returnCallerClass");
|
||||
* // expect Foo to be the caller class
|
||||
* var caller = csm.invoke(null);
|
||||
* assert(caller == Test.class);
|
||||
* }
|
||||
* void test2() throws Throwable {
|
||||
* // method handle for Method::invoke
|
||||
* MethodHandle mh = MethodHandles.lookup().findVirtual(Method.class, "invoke",
|
||||
* methodType(Object.class, Object.class, Object[].class));
|
||||
* var csm = CSM.class.getMethod("returnCallerClass");
|
||||
* // invoke Method::invoke via method handle and the target method
|
||||
* // being invoked reflectively is CSM::returnCallerClass
|
||||
* var caller = mh.invoke(csm, null, null);
|
||||
* assert(caller == Test.class);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*
|
||||
* Both CSM::returnCallerClass and Method::invoke can have an adapter method
|
||||
* with a caller-class parameter defined. Test::test calls CSM::returnCallerClass
|
||||
* via Method::invoke which does the stack walking to find the caller's class.
|
||||
* It will pass the caller's class directly to the adapter method for
|
||||
* CSM::returnCallerClass.
|
||||
*
|
||||
* Similarly, Test::test2 invokes the method handle of Method::invoke to
|
||||
* call CSM::returnCallerClass reflectively. In that case, MethodHandle::invokeExact
|
||||
* uses the lookup class of the Lookup object producing the method handle
|
||||
* as the caller's class, so no stack walking involved. The lookup class is Test.
|
||||
* It will invoke the adapter method of Method::invoke with Test as the caller's
|
||||
* class, which in turn invokes the adapter method of CSM::returnCallerClass
|
||||
* with Test as the caller. The calling sequence eliminates the need for
|
||||
* multiple stack walks when a caller-sensitive method is invoked reflectively.
|
||||
*
|
||||
* For caller-sensitive methods that require an exact caller class, the adapter
|
||||
* method must be defined for correctness. {@link java.lang.invoke.MethodHandles#lookup()}
|
||||
* and {@link ClassLoader#registerAsParallelCapable()} are the only two methods
|
||||
* in the JDK that need the exact caller class.
|
||||
*
|
||||
* On the other hand, for caller-sensitive methods that use the caller's class
|
||||
* for access checks or security permission checks, i.e., based on its runtime
|
||||
* package, defining loader, or protection domain, the adapter method is optional.
|
||||
*/
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target({METHOD})
|
||||
public @interface CallerSensitiveAdapter {
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.Hidden;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* MethodAccessor adapter for caller-sensitive methods which have
|
||||
* an alternate non-CSM method with the same method name but an additional
|
||||
* caller class argument.
|
||||
*
|
||||
* When a caller-sensitive method is called,
|
||||
* Method::invoke(Object target, Object[] args, Class<?> caller) will
|
||||
* be invoked with the caller class. If an adapter is present,
|
||||
* the adapter method with the caller class parameter will be called
|
||||
* instead.
|
||||
*/
|
||||
class CsMethodAccessorAdapter extends MethodAccessorImpl {
|
||||
private final Method csmAdapter;
|
||||
private final MethodAccessor accessor;
|
||||
|
||||
CsMethodAccessorAdapter(Method method, Method csmAdapter, MethodAccessor accessor) {
|
||||
assert Reflection.isCallerSensitive(method) && !Reflection.isCallerSensitive(csmAdapter);
|
||||
this.csmAdapter = csmAdapter;
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object obj, Object[] args)
|
||||
throws IllegalArgumentException, InvocationTargetException {
|
||||
throw new InternalError("caller-sensitive method invoked without explicit caller: " + csmAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ForceInline
|
||||
@Hidden
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller)
|
||||
throws IllegalArgumentException, InvocationTargetException {
|
||||
Object[] newArgs = new Object[args == null ? 1 : args.length + 1];
|
||||
newArgs[0] = caller;
|
||||
if (args != null) {
|
||||
System.arraycopy(args, 0, newArgs, 1, args.length);
|
||||
}
|
||||
return accessor.invoke(obj, newArgs);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -35,7 +35,6 @@ import java.util.Objects;
|
||||
|
||||
class DelegatingConstructorAccessorImpl extends ConstructorAccessorImpl {
|
||||
// initial non-null delegate
|
||||
@Stable
|
||||
private final ConstructorAccessorImpl initialDelegate;
|
||||
// alternative delegate: starts as null;
|
||||
// only single change from null -> non-null is guaranteed
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -35,10 +35,11 @@ import java.util.Objects;
|
||||
|
||||
class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
|
||||
// initial non-null delegate
|
||||
@Stable private final MethodAccessorImpl initialDelegate;
|
||||
private final MethodAccessorImpl initialDelegate;
|
||||
// alternative delegate: starts as null;
|
||||
// only single change from null -> non-null is guaranteed
|
||||
@Stable private MethodAccessorImpl altDelegate;
|
||||
@Stable
|
||||
private MethodAccessorImpl altDelegate;
|
||||
|
||||
DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
|
||||
initialDelegate = Objects.requireNonNull(delegate);
|
||||
@ -51,6 +52,13 @@ class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
|
||||
return delegate().invoke(obj, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller)
|
||||
throws IllegalArgumentException, InvocationTargetException
|
||||
{
|
||||
return delegate().invoke(obj, args, caller);
|
||||
}
|
||||
|
||||
private MethodAccessorImpl delegate() {
|
||||
var d = altDelegate;
|
||||
return d != null ? d : initialDelegate;
|
||||
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.Hidden;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import static jdk.internal.reflect.MethodHandleAccessorFactory.SPECIALIZED_PARAM_COUNT;
|
||||
|
||||
class DirectConstructorHandleAccessor extends ConstructorAccessorImpl {
|
||||
static ConstructorAccessorImpl constructorAccessor(Constructor<?> ctor, MethodHandle target) {
|
||||
return new DirectConstructorHandleAccessor(ctor, target);
|
||||
}
|
||||
|
||||
static ConstructorAccessorImpl nativeAccessor(Constructor<?> ctor) {
|
||||
return new NativeAccessor(ctor);
|
||||
}
|
||||
|
||||
private static final int PARAM_COUNT_MASK = 0x00FF;
|
||||
private static final int NONZERO_BIT = 0x8000_0000;
|
||||
|
||||
private final int paramFlags;
|
||||
private final MethodHandle target;
|
||||
|
||||
DirectConstructorHandleAccessor(Constructor<?> ctor, MethodHandle target) {
|
||||
this.paramFlags = (ctor.getParameterCount() & PARAM_COUNT_MASK) | NONZERO_BIT;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object newInstance(Object[] args) throws InstantiationException, InvocationTargetException {
|
||||
int argc = args != null ? args.length : 0;
|
||||
// only check argument count for specialized forms
|
||||
int paramCount = paramFlags & PARAM_COUNT_MASK;
|
||||
if (paramCount <= SPECIALIZED_PARAM_COUNT && argc != paramCount) {
|
||||
throw new IllegalArgumentException("wrong number of arguments: " + argc + " expected: " + paramCount);
|
||||
}
|
||||
try {
|
||||
return invokeImpl(args);
|
||||
} catch (ClassCastException|WrongMethodTypeException e) {
|
||||
if (isIllegalArgument(e))
|
||||
throw new IllegalArgumentException("argument type mismatch", e);
|
||||
else
|
||||
throw new InvocationTargetException(e);
|
||||
} catch (NullPointerException e) {
|
||||
if (isIllegalArgument(e))
|
||||
throw new IllegalArgumentException(e);
|
||||
else
|
||||
throw new InvocationTargetException(e);
|
||||
} catch (Throwable e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isIllegalArgument(RuntimeException ex) {
|
||||
return AccessorUtils.isIllegalArgument(DirectConstructorHandleAccessor.class, ex);
|
||||
}
|
||||
|
||||
@Hidden
|
||||
@ForceInline
|
||||
Object invokeImpl(Object[] args) throws Throwable {
|
||||
return switch (paramFlags & PARAM_COUNT_MASK) {
|
||||
case 0 -> target.invokeExact();
|
||||
case 1 -> target.invokeExact(args[0]);
|
||||
case 2 -> target.invokeExact(args[0], args[1]);
|
||||
case 3 -> target.invokeExact(args[0], args[1], args[2]);
|
||||
default -> target.invokeExact(args);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the constructor via native VM reflection
|
||||
*/
|
||||
static class NativeAccessor extends ConstructorAccessorImpl {
|
||||
private final Constructor<?> ctor;
|
||||
NativeAccessor(Constructor<?> ctor) {
|
||||
this.ctor = ctor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object newInstance(Object[] args) throws InstantiationException, InvocationTargetException {
|
||||
return newInstance0(ctor, args);
|
||||
}
|
||||
private static native Object newInstance0(Constructor<?> c, Object[] args)
|
||||
throws InstantiationException, InvocationTargetException;
|
||||
}
|
||||
}
|
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.Hidden;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import static java.lang.invoke.MethodType.genericMethodType;
|
||||
import static jdk.internal.reflect.MethodHandleAccessorFactory.SPECIALIZED_PARAM_COUNT;
|
||||
import static jdk.internal.reflect.MethodHandleAccessorFactory.LazyStaticHolder.JLIA;
|
||||
|
||||
class DirectMethodHandleAccessor extends MethodAccessorImpl {
|
||||
/**
|
||||
* Creates a MethodAccessorImpl for a non-native method.
|
||||
*/
|
||||
static MethodAccessorImpl methodAccessor(Method method, MethodHandle target) {
|
||||
assert !Modifier.isNative(method.getModifiers());
|
||||
|
||||
return new DirectMethodHandleAccessor(method, target, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates MethodAccessorImpl for the adapter method for a caller-sensitive method.
|
||||
* The given target method handle is the adapter method with the additional caller class
|
||||
* parameter.
|
||||
*/
|
||||
static MethodAccessorImpl callerSensitiveAdapter(Method original, MethodHandle target) {
|
||||
assert Reflection.isCallerSensitive(original);
|
||||
|
||||
// for CSM adapter method with the additional caller class parameter
|
||||
// creates the adaptive method accessor only.
|
||||
return new DirectMethodHandleAccessor(original, target, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates MethodAccessorImpl that invokes the given method via VM native reflection
|
||||
* support. This is used for native methods. It can be used for java methods
|
||||
* during early VM startup.
|
||||
*/
|
||||
static MethodAccessorImpl nativeAccessor(Method method, boolean callerSensitive) {
|
||||
return callerSensitive ? new NativeAccessor(method, findCSMethodAdapter(method))
|
||||
: new NativeAccessor(method);
|
||||
}
|
||||
|
||||
private static final int PARAM_COUNT_MASK = 0x00FF;
|
||||
private static final int HAS_CALLER_PARAM_BIT = 0x0100;
|
||||
private static final int IS_STATIC_BIT = 0x0200;
|
||||
private static final int NONZERO_BIT = 0x8000_0000;
|
||||
|
||||
private final Class<?> declaringClass;
|
||||
private final int paramCount;
|
||||
private final int flags;
|
||||
private final MethodHandle target;
|
||||
|
||||
DirectMethodHandleAccessor(Method method, MethodHandle target, boolean hasCallerParameter) {
|
||||
this.declaringClass = method.getDeclaringClass();
|
||||
this.paramCount = method.getParameterCount();
|
||||
this.flags = (hasCallerParameter ? HAS_CALLER_PARAM_BIT : 0) |
|
||||
(Modifier.isStatic(method.getModifiers()) ? IS_STATIC_BIT : 0);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ForceInline
|
||||
public Object invoke(Object obj, Object[] args) throws InvocationTargetException {
|
||||
if (!isStatic()) {
|
||||
checkReceiver(obj);
|
||||
}
|
||||
checkArgumentCount(paramCount, args);
|
||||
try {
|
||||
return invokeImpl(obj, args);
|
||||
} catch (ClassCastException | WrongMethodTypeException e) {
|
||||
if (isIllegalArgument(e)) {
|
||||
// No cause in IAE to be consistent with the old behavior
|
||||
throw new IllegalArgumentException("argument type mismatch");
|
||||
} else {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
if (isIllegalArgument(e)) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} else {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ForceInline
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller) throws InvocationTargetException {
|
||||
if (!isStatic()) {
|
||||
checkReceiver(obj);
|
||||
}
|
||||
checkArgumentCount(paramCount, args);
|
||||
try {
|
||||
return invokeImpl(obj, args, caller);
|
||||
} catch (ClassCastException | WrongMethodTypeException e) {
|
||||
if (isIllegalArgument(e)) {
|
||||
// No cause in IAE to be consistent with the old behavior
|
||||
throw new IllegalArgumentException("argument type mismatch");
|
||||
} else {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
if (isIllegalArgument(e)) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} else {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Hidden
|
||||
@ForceInline
|
||||
private Object invokeImpl(Object obj, Object[] args) throws Throwable {
|
||||
return switch (paramCount) {
|
||||
case 0 -> target.invokeExact(obj);
|
||||
case 1 -> target.invokeExact(obj, args[0]);
|
||||
case 2 -> target.invokeExact(obj, args[0], args[1]);
|
||||
case 3 -> target.invokeExact(obj, args[0], args[1], args[2]);
|
||||
default -> target.invokeExact(obj, args);
|
||||
};
|
||||
}
|
||||
|
||||
@Hidden
|
||||
@ForceInline
|
||||
private Object invokeImpl(Object obj, Object[] args, Class<?> caller) throws Throwable {
|
||||
if (hasCallerParameter()) {
|
||||
// caller-sensitive method is invoked through method with caller parameter
|
||||
return switch (paramCount) {
|
||||
case 0 -> target.invokeExact(obj, caller);
|
||||
case 1 -> target.invokeExact(obj, args[0], caller);
|
||||
case 2 -> target.invokeExact(obj, args[0], args[1], caller);
|
||||
case 3 -> target.invokeExact(obj, args[0], args[1], args[2], caller);
|
||||
default -> target.invokeExact(obj, args, caller);
|
||||
};
|
||||
} else {
|
||||
// caller-sensitive method is invoked through a per-caller invoker while
|
||||
// the target MH is always spreading the args
|
||||
var invoker = JLIA.reflectiveInvoker(caller);
|
||||
try {
|
||||
// invoke the target method handle via an invoker
|
||||
return invoker.invokeExact(target, obj, args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStatic() {
|
||||
return (flags & IS_STATIC_BIT) == IS_STATIC_BIT;
|
||||
}
|
||||
|
||||
private boolean hasCallerParameter() {
|
||||
return (flags & HAS_CALLER_PARAM_BIT) == HAS_CALLER_PARAM_BIT;
|
||||
}
|
||||
|
||||
private boolean isIllegalArgument(RuntimeException ex) {
|
||||
return AccessorUtils.isIllegalArgument(DirectMethodHandleAccessor.class, ex);
|
||||
}
|
||||
|
||||
private void checkReceiver(Object o) {
|
||||
// NOTE: will throw NullPointerException, as specified, if o is null
|
||||
if (!declaringClass.isAssignableFrom(o.getClass())) {
|
||||
throw new IllegalArgumentException("object is not an instance of declaring class");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the method via native VM reflection
|
||||
*/
|
||||
static class NativeAccessor extends MethodAccessorImpl {
|
||||
private final Method method;
|
||||
private final Method csmAdapter;
|
||||
private final boolean callerSensitive;
|
||||
NativeAccessor(Method method) {
|
||||
assert !Reflection.isCallerSensitive(method);
|
||||
this.method = method;
|
||||
this.csmAdapter = null;
|
||||
this.callerSensitive = false;
|
||||
}
|
||||
|
||||
NativeAccessor(Method method, Method csmAdapter) {
|
||||
assert Reflection.isCallerSensitive(method);
|
||||
this.method = method;
|
||||
this.csmAdapter = csmAdapter;
|
||||
this.callerSensitive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object obj, Object[] args) throws InvocationTargetException {
|
||||
assert csmAdapter == null;
|
||||
return invoke0(method, obj, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller) throws InvocationTargetException {
|
||||
assert callerSensitive;
|
||||
|
||||
if (csmAdapter != null) {
|
||||
Object[] newArgs = new Object[csmAdapter.getParameterCount()];
|
||||
newArgs[0] = caller;
|
||||
if (args != null) {
|
||||
System.arraycopy(args, 0, newArgs, 1, args.length);
|
||||
}
|
||||
return invoke0(csmAdapter, obj, newArgs);
|
||||
} else {
|
||||
assert VM.isJavaLangInvokeInited();
|
||||
try {
|
||||
return ReflectiveInvoker.invoke(methodAccessorInvoker(), caller, obj, args);
|
||||
} catch (InvocationTargetException|RuntimeException|Error e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object invokeViaReflectiveInvoker(Object obj, Object[] args) throws InvocationTargetException {
|
||||
return invoke0(method, obj, args);
|
||||
}
|
||||
|
||||
/*
|
||||
* A method handle to invoke Reflective::Invoker
|
||||
*/
|
||||
private MethodHandle maInvoker;
|
||||
private MethodHandle methodAccessorInvoker() {
|
||||
MethodHandle invoker = maInvoker;
|
||||
if (invoker == null) {
|
||||
maInvoker = invoker = ReflectiveInvoker.bindTo(this);
|
||||
}
|
||||
return invoker;
|
||||
}
|
||||
|
||||
private static native Object invoke0(Method m, Object obj, Object[] args);
|
||||
|
||||
static class ReflectiveInvoker {
|
||||
/**
|
||||
* Return a method handle for NativeAccessor::invoke bound to the given accessor object
|
||||
*/
|
||||
static MethodHandle bindTo(NativeAccessor accessor) {
|
||||
return NATIVE_ACCESSOR_INVOKE.bindTo(accessor);
|
||||
}
|
||||
|
||||
/*
|
||||
* When Method::invoke on a caller-sensitive method is to be invoked
|
||||
* and no adapter method with an additional caller class argument is defined,
|
||||
* the caller-sensitive method must be invoked via an invoker injected
|
||||
* which has the following signature:
|
||||
* reflect_invoke_V(MethodHandle mh, Object target, Object[] args)
|
||||
*
|
||||
* The stack frames calling the method `csm` through reflection will
|
||||
* look like this:
|
||||
* obj.csm(args)
|
||||
* NativeAccessor::invoke(obj, args)
|
||||
* InjectedInvoker::reflect_invoke_V(vamh, obj, args);
|
||||
* method::invoke(obj, args)
|
||||
* p.Foo::m
|
||||
*
|
||||
* An injected invoker class is a hidden class which has the same
|
||||
* defining class loader, runtime package, and protection domain
|
||||
* as the given caller class.
|
||||
*
|
||||
* The caller-sensitive method will call Reflection::getCallerClass
|
||||
* to get the caller class.
|
||||
*/
|
||||
static Object invoke(MethodHandle target, Class<?> caller, Object obj, Object[] args)
|
||||
throws InvocationTargetException
|
||||
{
|
||||
var reflectInvoker = JLIA.reflectiveInvoker(caller);
|
||||
try {
|
||||
return reflectInvoker.invokeExact(target, obj, args);
|
||||
} catch (InvocationTargetException | RuntimeException | Error e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
static final JavaLangInvokeAccess JLIA;
|
||||
static final MethodHandle NATIVE_ACCESSOR_INVOKE;
|
||||
static {
|
||||
try {
|
||||
JLIA = SharedSecrets.getJavaLangInvokeAccess();
|
||||
NATIVE_ACCESSOR_INVOKE = MethodHandles.lookup().findVirtual(NativeAccessor.class, "invoke",
|
||||
genericMethodType(1, true));
|
||||
} catch (NoSuchMethodException|IllegalAccessException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkArgumentCount(int paramCount, Object[] args) {
|
||||
// only check argument count for specialized forms
|
||||
if (paramCount > SPECIALIZED_PARAM_COUNT) return;
|
||||
|
||||
int argc = args != null ? args.length : 0;
|
||||
if (argc != paramCount) {
|
||||
throw new IllegalArgumentException("wrong number of arguments: " + argc + " expected: " + paramCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an adapter for caller-sensitive method if present.
|
||||
* Otherwise, null.
|
||||
*
|
||||
* A trusted method can define an adapter method for a caller-sensitive method `foo`
|
||||
* with an additional caller class argument that will be invoked reflectively.
|
||||
*/
|
||||
private static Method findCSMethodAdapter(Method method) {
|
||||
if (!Reflection.isCallerSensitive(method)) return null;
|
||||
|
||||
int paramCount = method.getParameterCount();
|
||||
Class<?>[] ptypes = new Class<?>[paramCount+1];
|
||||
ptypes[paramCount] = Class.class;
|
||||
System.arraycopy(method.getParameterTypes(), 0, ptypes, 0, paramCount);
|
||||
try {
|
||||
return method.getDeclaringClass().getDeclaredMethod(method.getName(), ptypes);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,9 +25,8 @@
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
/** Package-private implementation of the FieldAccessor interface
|
||||
which has access to all classes and all fields, regardless of
|
||||
@ -35,7 +34,6 @@ import java.lang.reflect.Field;
|
||||
|
||||
abstract class FieldAccessorImpl extends MagicAccessorImpl
|
||||
implements FieldAccessor {
|
||||
@Stable
|
||||
protected final Field field;
|
||||
|
||||
FieldAccessorImpl(Field field) {
|
||||
@ -113,4 +111,167 @@ abstract class FieldAccessorImpl extends MagicAccessorImpl
|
||||
/** Matches specification in {@link java.lang.reflect.Field} */
|
||||
public abstract void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException;
|
||||
|
||||
|
||||
protected void ensureObj(Object o) {
|
||||
// NOTE: will throw NullPointerException, as specified, if o is null
|
||||
if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
|
||||
throwSetIllegalArgumentException(o);
|
||||
}
|
||||
}
|
||||
|
||||
private String getQualifiedFieldName() {
|
||||
return field.getDeclaringClass().getName() + "." +field.getName();
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetIllegalArgumentException(String type) {
|
||||
return new IllegalArgumentException(
|
||||
"Attempt to get "+field.getType().getName()+" field \"" +
|
||||
getQualifiedFieldName() + "\" with illegal data type conversion to "+type
|
||||
);
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(String attemptedType,
|
||||
String attemptedValue)
|
||||
throws IllegalAccessException {
|
||||
throw new IllegalAccessException(getSetMessage(attemptedType, attemptedValue));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(Object o) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException(o != null ? o.getClass().getName() : "", "");
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(boolean z) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("boolean", Boolean.toString(z));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(char b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("char", Character.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(byte b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("byte", Byte.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(short b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("short", Short.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(int i) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("int", Integer.toString(i));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(long i) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("long", Long.toString(i));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(float f) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("float", Float.toString(f));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(double f) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("double", Double.toString(f));
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetBooleanIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("boolean");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetByteIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("byte");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetCharIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("char");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetShortIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("short");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetIntIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("int");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetLongIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("long");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetFloatIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("float");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetDoubleIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("double");
|
||||
}
|
||||
|
||||
protected String getSetMessage(String attemptedType, String attemptedValue) {
|
||||
String err = "Can not set";
|
||||
if (Modifier.isStatic(field.getModifiers()))
|
||||
err += " static";
|
||||
if (Modifier.isFinal(field.getModifiers()))
|
||||
err += " final";
|
||||
err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " to ";
|
||||
if (!attemptedValue.isEmpty()) {
|
||||
err += "(" + attemptedType + ")" + attemptedValue;
|
||||
} else {
|
||||
if (!attemptedType.isEmpty())
|
||||
err += attemptedType;
|
||||
else
|
||||
err += "null value";
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
protected String getMessage(boolean getter, String attemptedType) {
|
||||
String err = "Can not " + (getter ? "get" : "set");
|
||||
if (Modifier.isStatic(field.getModifiers()))
|
||||
err += " static";
|
||||
if (Modifier.isFinal(field.getModifiers()))
|
||||
err += " final";
|
||||
err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " on " + attemptedType;
|
||||
return err;
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(String attemptedType,
|
||||
String attemptedValue) {
|
||||
throw new IllegalArgumentException(getSetMessage(attemptedType,attemptedValue));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(Object o) {
|
||||
throwSetIllegalArgumentException(o != null ? o.getClass().getName() : "", "");
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(boolean b) {
|
||||
throwSetIllegalArgumentException("boolean", Boolean.toString(b));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(byte b) {
|
||||
throwSetIllegalArgumentException("byte", Byte.toString(b));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(char c) {
|
||||
throwSetIllegalArgumentException("char", Character.toString(c));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(short s) {
|
||||
throwSetIllegalArgumentException("short", Short.toString(s));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(int i) {
|
||||
throwSetIllegalArgumentException("int", Integer.toString(i));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(long l) {
|
||||
throwSetIllegalArgumentException("long", Long.toString(l));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(float f) {
|
||||
throwSetIllegalArgumentException("float", Float.toString(f));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(double d) {
|
||||
throwSetIllegalArgumentException("double", Double.toString(d));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,4 +37,7 @@ public interface MethodAccessor {
|
||||
/** Matches specification in {@link java.lang.reflect.Method} */
|
||||
public Object invoke(Object obj, Object[] args)
|
||||
throws IllegalArgumentException, InvocationTargetException;
|
||||
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller)
|
||||
throws IllegalArgumentException, InvocationTargetException;
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ package jdk.internal.reflect;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/** Generator for sun.reflect.MethodAccessor and
|
||||
sun.reflect.ConstructorAccessor objects using bytecodes to
|
||||
/** Generator for jdk.internal.reflect.MethodAccessor and
|
||||
jdk.internal.reflect.ConstructorAccessor objects using bytecodes to
|
||||
implement reflection. A java.lang.reflect.Method or
|
||||
java.lang.reflect.Constructor object can delegate its invoke or
|
||||
newInstance method to an accessor using native code or to one
|
||||
|
@ -45,4 +45,9 @@ abstract class MethodAccessorImpl extends MagicAccessorImpl
|
||||
/** Matches specification in {@link java.lang.reflect.Method} */
|
||||
public abstract Object invoke(Object obj, Object[] args)
|
||||
throws IllegalArgumentException, InvocationTargetException;
|
||||
|
||||
public Object invoke(Object obj, Object[] args, Class<?> caller)
|
||||
throws IllegalArgumentException, InvocationTargetException {
|
||||
return invoke(obj, args);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,366 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.misc.VM;
|
||||
|
||||
import static java.lang.invoke.MethodType.genericMethodType;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
import static jdk.internal.reflect.MethodHandleAccessorFactory.LazyStaticHolder.*;
|
||||
|
||||
final class MethodHandleAccessorFactory {
|
||||
/**
|
||||
* Creates a MethodAccessor for the given reflected method.
|
||||
*
|
||||
* If the given method is called before the java.lang.invoke initialization
|
||||
* or the given method is a native method, it will use the native VM reflection
|
||||
* support.
|
||||
*
|
||||
* If the given method is a caller-sensitive method and the corresponding
|
||||
* caller-sensitive adapter with the caller class parameter is present,
|
||||
* it will use the method handle of the caller-sensitive adapter.
|
||||
*
|
||||
* Otherwise, it will use the direct method handle of the given method.
|
||||
*
|
||||
* @see CallerSensitive
|
||||
* @see CallerSensitiveAdapter
|
||||
*/
|
||||
static MethodAccessorImpl newMethodAccessor(Method method, boolean callerSensitive) {
|
||||
if (useNativeAccessor(method)) {
|
||||
return DirectMethodHandleAccessor.nativeAccessor(method, callerSensitive);
|
||||
}
|
||||
|
||||
// ExceptionInInitializerError may be thrown during class initialization
|
||||
// Ensure class initialized outside the invocation of method handle
|
||||
// so that EIIE is propagated (not wrapped with ITE)
|
||||
ensureClassInitialized(method.getDeclaringClass());
|
||||
|
||||
try {
|
||||
if (callerSensitive) {
|
||||
var dmh = findCallerSensitiveAdapter(method);
|
||||
if (dmh != null) {
|
||||
return DirectMethodHandleAccessor.callerSensitiveAdapter(method, dmh);
|
||||
}
|
||||
}
|
||||
var dmh = getDirectMethod(method, callerSensitive);
|
||||
return DirectMethodHandleAccessor.methodAccessor(method, dmh);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ConstructorAccessor for the given reflected constructor.
|
||||
*
|
||||
* If a given constructor is called before the java.lang.invoke initialization,
|
||||
* it will use the native VM reflection support.
|
||||
*
|
||||
* Otherwise, it will use the direct method handle of the given constructor.
|
||||
*/
|
||||
static ConstructorAccessorImpl newConstructorAccessor(Constructor<?> ctor) {
|
||||
if (useNativeAccessor(ctor)) {
|
||||
return DirectConstructorHandleAccessor.nativeAccessor(ctor);
|
||||
}
|
||||
|
||||
// ExceptionInInitializerError may be thrown during class initialization
|
||||
// Ensure class initialized outside the invocation of method handle
|
||||
// so that EIIE is propagated (not wrapped with ITE)
|
||||
ensureClassInitialized(ctor.getDeclaringClass());
|
||||
|
||||
try {
|
||||
MethodHandle mh = JLIA.unreflectConstructor(ctor);
|
||||
int paramCount = mh.type().parameterCount();
|
||||
MethodHandle target = mh.asFixedArity();
|
||||
MethodType mtype = specializedMethodTypeForConstructor(paramCount);
|
||||
if (paramCount > SPECIALIZED_PARAM_COUNT) {
|
||||
// spread the parameters only for the non-specialized case
|
||||
target = target.asSpreader(Object[].class, paramCount);
|
||||
}
|
||||
target = target.asType(mtype);
|
||||
return DirectConstructorHandleAccessor.constructorAccessor(ctor, target);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a FieldAccessor for the given reflected field.
|
||||
*
|
||||
* Limitation: Field access via core reflection is only supported after
|
||||
* java.lang.invoke completes initialization.
|
||||
* java.lang.invoke initialization starts soon after System::initPhase1
|
||||
* and method handles are ready for use when initPhase2 begins.
|
||||
* During early VM startup (initPhase1), fields can be accessed directly
|
||||
* from the VM or through JNI.
|
||||
*/
|
||||
static FieldAccessorImpl newFieldAccessor(Field field, boolean isReadOnly) {
|
||||
if (!VM.isJavaLangInvokeInited()) {
|
||||
throw new InternalError(field.getDeclaringClass().getName() + "::" + field.getName() +
|
||||
" cannot be accessed reflectively before java.lang.invoke is initialized");
|
||||
}
|
||||
|
||||
// ExceptionInInitializerError may be thrown during class initialization
|
||||
// Ensure class initialized outside the invocation of method handle
|
||||
// so that EIIE is propagated (not wrapped with ITE)
|
||||
ensureClassInitialized(field.getDeclaringClass());
|
||||
|
||||
try {
|
||||
// the declaring class of the field has been initialized
|
||||
var getter = JLIA.unreflectField(field, false);
|
||||
var setter = isReadOnly ? null : JLIA.unreflectField(field, true);
|
||||
Class<?> type = field.getType();
|
||||
if (type == Boolean.TYPE) {
|
||||
return MethodHandleBooleanFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Byte.TYPE) {
|
||||
return MethodHandleByteFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Short.TYPE) {
|
||||
return MethodHandleShortFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Character.TYPE) {
|
||||
return MethodHandleCharacterFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Integer.TYPE) {
|
||||
return MethodHandleIntegerFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Long.TYPE) {
|
||||
return MethodHandleLongFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Float.TYPE) {
|
||||
return MethodHandleFloatFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else if (type == Double.TYPE) {
|
||||
return MethodHandleDoubleFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
} else {
|
||||
return MethodHandleObjectFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodHandle getDirectMethod(Method method, boolean callerSensitive) throws IllegalAccessException {
|
||||
var mtype = methodType(method.getReturnType(), method.getParameterTypes());
|
||||
var isStatic = Modifier.isStatic(method.getModifiers());
|
||||
var dmh = isStatic ? JLIA.findStatic(method.getDeclaringClass(), method.getName(), mtype)
|
||||
: JLIA.findVirtual(method.getDeclaringClass(), method.getName(), mtype);
|
||||
if (callerSensitive) {
|
||||
// the reflectiveInvoker for caller-sensitive method expects the same signature
|
||||
// as Method::invoke i.e. (Object, Object[])Object
|
||||
return makeTarget(dmh, isStatic, false);
|
||||
}
|
||||
return makeSpecializedTarget(dmh, isStatic, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the method handle of a caller-sensitive adapter for the given
|
||||
* caller-sensitive method. It has the same name as the given method
|
||||
* with a trailing caller class parameter.
|
||||
*
|
||||
* @see CallerSensitiveAdapter
|
||||
*/
|
||||
private static MethodHandle findCallerSensitiveAdapter(Method method) throws IllegalAccessException {
|
||||
String name = method.getName();
|
||||
// append a Class parameter
|
||||
MethodType mtype = methodType(method.getReturnType(), method.getParameterTypes())
|
||||
.appendParameterTypes(Class.class);
|
||||
boolean isStatic = Modifier.isStatic(method.getModifiers());
|
||||
|
||||
MethodHandle dmh = isStatic ? JLIA.findStatic(method.getDeclaringClass(), name, mtype)
|
||||
: JLIA.findVirtual(method.getDeclaringClass(), name, mtype);
|
||||
return dmh != null ? makeSpecializedTarget(dmh, isStatic, true) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the given dmh to a specialized target method handle.
|
||||
*
|
||||
* If {@code hasCallerParameter} parameter is true, transform the method handle
|
||||
* of this method type: {@code (Object, Object[], Class)Object} for the default
|
||||
* case.
|
||||
*
|
||||
* If {@code hasCallerParameter} parameter is false, transform the method handle
|
||||
* of this method type: {@code (Object, Object[])Object} for the default case.
|
||||
*
|
||||
* If the number of formal arguments is small, use a method type specialized
|
||||
* the number of formal arguments is 0, 1, and 2, for example, the method type
|
||||
* of a static method with one argument can be: {@code (Object)Object}
|
||||
*
|
||||
* If it's a static method, there is no leading Object parameter.
|
||||
*
|
||||
* @apiNote
|
||||
* This implementation avoids using MethodHandles::catchException to help
|
||||
* cold startup performance since this combination is very costly to setup.
|
||||
*
|
||||
* @param dmh DirectMethodHandle
|
||||
* @param isStatic whether given dmh represents static method or not
|
||||
* @param hasCallerParameter whether given dmh represents a method with an
|
||||
* additional caller Class parameter
|
||||
* @return transformed dmh to be used as a target in direct method accessors
|
||||
*/
|
||||
static MethodHandle makeSpecializedTarget(MethodHandle dmh, boolean isStatic, boolean hasCallerParameter) {
|
||||
MethodHandle target = dmh.asFixedArity();
|
||||
|
||||
// number of formal arguments to the original method (not the adapter)
|
||||
// If it is a non-static method, it has a leading `this` argument.
|
||||
// Also do not count the caller class argument
|
||||
int paramCount = dmh.type().parameterCount() - (isStatic ? 0 : 1) - (hasCallerParameter ? 1 : 0);
|
||||
MethodType mtype = specializedMethodType(isStatic, hasCallerParameter, paramCount);
|
||||
if (paramCount > SPECIALIZED_PARAM_COUNT) {
|
||||
int spreadArgPos = isStatic ? 0 : 1;
|
||||
target = target.asSpreader(spreadArgPos, Object[].class, paramCount);
|
||||
}
|
||||
if (isStatic) {
|
||||
// add leading 'this' parameter to static method which is then ignored
|
||||
target = MethodHandles.dropArguments(target, 0, Object.class);
|
||||
}
|
||||
return target.asType(mtype);
|
||||
}
|
||||
|
||||
// specialize for number of formal arguments <= 3 to avoid spreader
|
||||
static final int SPECIALIZED_PARAM_COUNT = 3;
|
||||
static MethodType specializedMethodType(boolean isStatic, boolean hasCallerParameter, int paramCount) {
|
||||
return switch (paramCount) {
|
||||
case 0 -> hasCallerParameter ? methodType(Object.class, Object.class, Class.class)
|
||||
: genericMethodType(1);
|
||||
case 1 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Class.class)
|
||||
: genericMethodType(2);
|
||||
case 2 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Object.class, Class.class)
|
||||
: genericMethodType(3);
|
||||
case 3 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Object.class, Object.class, Class.class)
|
||||
: genericMethodType(4);
|
||||
default -> hasCallerParameter ? methodType(Object.class, Object.class, Object[].class, Class.class)
|
||||
: genericMethodType(1, true);
|
||||
};
|
||||
}
|
||||
|
||||
static MethodType specializedMethodTypeForConstructor(int paramCount) {
|
||||
return switch (paramCount) {
|
||||
case 0 -> genericMethodType(0);
|
||||
case 1 -> genericMethodType(1);
|
||||
case 2 -> genericMethodType(2);
|
||||
case 3 -> genericMethodType(3);
|
||||
default -> genericMethodType(0, true);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the given dmh into a target method handle with the method type
|
||||
* {@code (Object, Object[])Object} or {@code (Object, Class, Object[])Object}
|
||||
*/
|
||||
static MethodHandle makeTarget(MethodHandle dmh, boolean isStatic, boolean hasCallerParameter) {
|
||||
MethodType mtype = hasCallerParameter
|
||||
? methodType(Object.class, Object.class, Object[].class, Class.class)
|
||||
: genericMethodType(1, true);
|
||||
// number of formal arguments
|
||||
int paramCount = dmh.type().parameterCount() - (isStatic ? 0 : 1) - (hasCallerParameter ? 1 : 0);
|
||||
int spreadArgPos = isStatic ? 0 : 1;
|
||||
MethodHandle target = dmh.asFixedArity().asSpreader(spreadArgPos, Object[].class, paramCount);
|
||||
if (isStatic) {
|
||||
// add leading 'this' parameter to static method which is then ignored
|
||||
target = MethodHandles.dropArguments(target, 0, Object.class);
|
||||
}
|
||||
return target.asType(mtype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the given class is initialized. If this is called from <clinit>,
|
||||
* this method returns but defc's class initialization is not completed.
|
||||
*/
|
||||
static void ensureClassInitialized(Class<?> defc) {
|
||||
if (UNSAFE.shouldBeInitialized(defc)) {
|
||||
UNSAFE.ensureClassInitialized(defc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if NativeAccessor should be used.
|
||||
*/
|
||||
private static boolean useNativeAccessor(Executable member) {
|
||||
if (!VM.isJavaLangInvokeInited())
|
||||
return true;
|
||||
|
||||
if (Modifier.isNative(member.getModifiers()))
|
||||
return true;
|
||||
|
||||
if (ReflectionFactory.useNativeAccessorOnly()) // for testing only
|
||||
return true;
|
||||
|
||||
// MethodHandle::withVarargs on a member with varargs modifier bit set
|
||||
// verifies that the last parameter of the member must be an array type.
|
||||
// The JVMS does not require the last parameter descriptor of the method descriptor
|
||||
// is an array type if the ACC_VARARGS flag is set in the access_flags item.
|
||||
// Hence the reflection implementation does not check the last parameter type
|
||||
// if ACC_VARARGS flag is set. Workaround this by invoking through
|
||||
// the native accessor.
|
||||
int paramCount = member.getParameterCount();
|
||||
if (member.isVarArgs() &&
|
||||
(paramCount == 0 || !(member.getParameterTypes()[paramCount-1].isArray()))) {
|
||||
return true;
|
||||
}
|
||||
// A method handle cannot be created if its type has an arity >= 255
|
||||
// as the method handle's invoke method consumes an extra argument
|
||||
// of the method handle itself. Fall back to use the native implementation.
|
||||
if (slotCount(member) >= MAX_JVM_ARITY) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final int MAX_JVM_ARITY = 255; // this is mandated by the JVM spec.
|
||||
/*
|
||||
* Return number of slots of the given member.
|
||||
* - long/double args counts for two argument slots
|
||||
* - A non-static method consumes an extra argument for the object on which
|
||||
* the method is called.
|
||||
* - A constructor consumes an extra argument for the object which is being constructed.
|
||||
*/
|
||||
private static int slotCount(Executable member) {
|
||||
int slots = 0;
|
||||
Class<?>[] ptypes = member.getParameterTypes();
|
||||
for (Class<?> ptype : ptypes) {
|
||||
if (ptype == double.class || ptype == long.class) {
|
||||
slots++;
|
||||
}
|
||||
}
|
||||
return ptypes.length + slots +
|
||||
(Modifier.isStatic(member.getModifiers()) ? 0 : 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delay initializing these static fields until java.lang.invoke is fully initialized.
|
||||
*/
|
||||
static class LazyStaticHolder {
|
||||
static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
|
||||
}
|
||||
|
||||
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleBooleanFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(boolean.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, boolean.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(boolean.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, boolean.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleBooleanFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleBooleanFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Boolean.valueOf(getBoolean(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
ensureObj(obj);
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (boolean) getter.invokeExact();
|
||||
} else {
|
||||
return (boolean) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
throw newGetIntIllegalArgumentException();
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
throw newGetLongIllegalArgumentException();
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
throw newGetFloatIllegalArgumentException();
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
throw newGetDoubleIllegalArgumentException();
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
ensureObj(obj);
|
||||
if (isReadOnly()) {
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Boolean b) {
|
||||
setBoolean(obj, b.booleanValue());
|
||||
} else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
ensureObj(obj);
|
||||
if (isReadOnly()) {
|
||||
throwFinalFieldIllegalAccessException(z);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(z);
|
||||
} else {
|
||||
setter.invokeExact(obj, z);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleByteFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(byte.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, byte.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(byte.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, byte.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleByteFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleByteFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Byte.valueOf(getByte(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (byte) getter.invokeExact();
|
||||
} else {
|
||||
return (byte) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
return getByte(obj);
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
return getByte(obj);
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
return getByte(obj);
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
return getByte(obj);
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getByte(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
ensureObj(obj);
|
||||
if (isReadOnly()) {
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setByte(obj, b.byteValue());
|
||||
} else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
ensureObj(obj);
|
||||
if (isReadOnly()) {
|
||||
throwFinalFieldIllegalAccessException(b);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(b);
|
||||
} else {
|
||||
setter.invokeExact(obj, b);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleCharacterFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(char.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, char.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(char.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, char.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleCharacterFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleCharacterFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Character.valueOf(getChar(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (char) getter.invokeExact();
|
||||
} else {
|
||||
return (char) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
return getChar(obj);
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
return getChar(obj);
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
return getChar(obj);
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getChar(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Character c) {
|
||||
setChar(obj, c.charValue());
|
||||
} else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(c);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(c);
|
||||
} else {
|
||||
setter.invokeExact(obj, c);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleDoubleFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(double.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, double.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(double.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, double.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleDoubleFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleDoubleFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Double.valueOf(getDouble(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
throw newGetIntIllegalArgumentException();
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
throw newGetLongIllegalArgumentException();
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
throw newGetFloatIllegalArgumentException();
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (double) getter.invokeExact();
|
||||
} else {
|
||||
return (double) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setDouble(obj, b.byteValue());
|
||||
}
|
||||
else if (value instanceof Short s) {
|
||||
setDouble(obj, s.shortValue());
|
||||
}
|
||||
else if (value instanceof Character c) {
|
||||
setDouble(obj, c.charValue());
|
||||
}
|
||||
else if (value instanceof Integer i) {
|
||||
setDouble(obj, i.intValue());
|
||||
}
|
||||
else if (value instanceof Long l) {
|
||||
setDouble(obj, l.longValue());
|
||||
}
|
||||
else if (value instanceof Float f) {
|
||||
setDouble(obj, f.floatValue());
|
||||
}
|
||||
else if (value instanceof Double d) {
|
||||
setDouble(obj, d.doubleValue());
|
||||
}
|
||||
else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setDouble(obj, f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(d);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(d);
|
||||
} else {
|
||||
setter.invokeExact(obj, d);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
abstract class MethodHandleFieldAccessorImpl extends FieldAccessorImpl {
|
||||
private static final int IS_READ_ONLY_BIT = 0x0001;
|
||||
private static final int IS_STATIC_BIT = 0x0002;
|
||||
private static final int NONZERO_BIT = 0x8000;
|
||||
|
||||
private final int fieldFlags;
|
||||
protected final MethodHandle getter;
|
||||
protected final MethodHandle setter;
|
||||
|
||||
protected MethodHandleFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field);
|
||||
this.fieldFlags = (isReadOnly ? IS_READ_ONLY_BIT : 0) |
|
||||
(isStatic ? IS_STATIC_BIT : 0) |
|
||||
NONZERO_BIT;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
protected final boolean isReadOnly() {
|
||||
return (fieldFlags & IS_READ_ONLY_BIT) == IS_READ_ONLY_BIT;
|
||||
}
|
||||
|
||||
protected final boolean isStatic() {
|
||||
return (fieldFlags & IS_STATIC_BIT) == IS_STATIC_BIT;
|
||||
}
|
||||
|
||||
protected final void ensureObj(Object o) {
|
||||
if (!isStatic()) {
|
||||
// for compatibility, check the receiver object first
|
||||
// throw NullPointerException if o is null
|
||||
if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
|
||||
throwSetIllegalArgumentException(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IllegalArgumentException because Field::get on the specified object, which
|
||||
* is not an instance of the class or interface declaring the underlying method
|
||||
*/
|
||||
protected IllegalArgumentException newGetIllegalArgumentException(Class<?> type) {
|
||||
return new IllegalArgumentException(getMessage(true, type.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* IllegalArgumentException because Field::set on the specified object, which
|
||||
* is not an instance of the class or interface declaring the underlying method
|
||||
*/
|
||||
protected IllegalArgumentException newSetIllegalArgumentException(Class<?> type) {
|
||||
return new IllegalArgumentException(getMessage(false, type.getName()));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleFloatFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(float.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, float.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(float.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, float.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleFloatFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleFloatFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Float.valueOf(getFloat(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
throw newGetIntIllegalArgumentException();
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
throw newGetLongIllegalArgumentException();
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (float) getter.invokeExact();
|
||||
} else {
|
||||
return (float) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getFloat(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setFloat(obj, b.byteValue());
|
||||
}
|
||||
else if (value instanceof Short s) {
|
||||
setFloat(obj, s.shortValue());
|
||||
}
|
||||
else if (value instanceof Character c) {
|
||||
setFloat(obj, c.charValue());
|
||||
}
|
||||
else if (value instanceof Integer i) {
|
||||
setFloat(obj, i.intValue());
|
||||
}
|
||||
else if (value instanceof Long l) {
|
||||
setFloat(obj, l.longValue());
|
||||
}
|
||||
else if (value instanceof Float f) {
|
||||
setFloat(obj, f.floatValue());
|
||||
}
|
||||
else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setFloat(obj, b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setFloat(obj, c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setFloat(obj, s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setFloat(obj, i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setFloat(obj, l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(f);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(f);
|
||||
} else {
|
||||
setter.invokeExact(obj, f);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleIntegerFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(int.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, int.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(int.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, int.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleIntegerFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleIntegerFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Integer.valueOf(getInt(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (int) getter.invokeExact();
|
||||
} else {
|
||||
return (int) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
return getInt(obj);
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
return getInt(obj);
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getInt(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setInt(obj, b.byteValue());
|
||||
}
|
||||
else if (value instanceof Short s) {
|
||||
setInt(obj, s.shortValue());
|
||||
}
|
||||
else if (value instanceof Character c) {
|
||||
setInt(obj, c.charValue());
|
||||
}
|
||||
else if (value instanceof Integer i) {
|
||||
setInt(obj, i.intValue());
|
||||
}
|
||||
else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setInt(obj, b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setInt(obj, c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setInt(obj, s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(i);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(i);
|
||||
} else {
|
||||
setter.invokeExact(obj, i);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleLongFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(long.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, long.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(long.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, long.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleLongFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleLongFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Long.valueOf(getLong(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
throw newGetIntIllegalArgumentException();
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (long) getter.invokeExact();
|
||||
} else {
|
||||
return (long) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
return getLong(obj);
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getLong(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setLong(obj, b.byteValue());
|
||||
}
|
||||
else if (value instanceof Short s) {
|
||||
setLong(obj, s.shortValue());
|
||||
}
|
||||
else if (value instanceof Character c) {
|
||||
setLong(obj, c.charValue());
|
||||
}
|
||||
else if (value instanceof Integer i) {
|
||||
setLong(obj, i.intValue());
|
||||
}
|
||||
else if (value instanceof Long l) {
|
||||
setLong(obj, l.longValue());
|
||||
}
|
||||
else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setLong(obj, b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setLong(obj, c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setLong(obj, s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setLong(obj, i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(l);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(l);
|
||||
} else {
|
||||
setter.invokeExact(obj, l);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleObjectFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(Object.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, Object.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleObjectFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleObjectFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
return isStatic() ? getter.invokeExact() : getter.invokeExact(obj);
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
throw newGetShortIllegalArgumentException();
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
throw newGetIntIllegalArgumentException();
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
throw newGetLongIllegalArgumentException();
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
throw newGetFloatIllegalArgumentException();
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
throw newGetDoubleIllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object obj, Object value) throws IllegalAccessException {
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(value);
|
||||
} else {
|
||||
setter.invokeExact(obj, value);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(s);
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class MethodHandleShortFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
|
||||
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if (isStatic) {
|
||||
getter = getter.asType(MethodType.methodType(short.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, short.class));
|
||||
}
|
||||
} else {
|
||||
getter = getter.asType(MethodType.methodType(short.class, Object.class));
|
||||
if (setter != null) {
|
||||
setter = setter.asType(MethodType.methodType(void.class, Object.class, short.class));
|
||||
}
|
||||
}
|
||||
return new MethodHandleShortFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
MethodHandleShortFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
|
||||
super(field, getter, setter, isReadOnly, isStatic);
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return Short.valueOf(getShort(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
throw newGetBooleanIllegalArgumentException();
|
||||
}
|
||||
|
||||
public byte getByte(Object obj) throws IllegalArgumentException {
|
||||
throw newGetByteIllegalArgumentException();
|
||||
}
|
||||
|
||||
public char getChar(Object obj) throws IllegalArgumentException {
|
||||
throw newGetCharIllegalArgumentException();
|
||||
}
|
||||
|
||||
public short getShort(Object obj) throws IllegalArgumentException {
|
||||
try {
|
||||
if (isStatic()) {
|
||||
return (short) getter.invokeExact();
|
||||
} else {
|
||||
return (short) getter.invokeExact(obj);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newGetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getInt(Object obj) throws IllegalArgumentException {
|
||||
return getShort(obj);
|
||||
}
|
||||
|
||||
public long getLong(Object obj) throws IllegalArgumentException {
|
||||
return getShort(obj);
|
||||
}
|
||||
|
||||
public float getFloat(Object obj) throws IllegalArgumentException {
|
||||
return getShort(obj);
|
||||
}
|
||||
|
||||
public double getDouble(Object obj) throws IllegalArgumentException {
|
||||
return getShort(obj);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(value);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
|
||||
if (value instanceof Byte b) {
|
||||
setShort(obj, b.byteValue());
|
||||
}
|
||||
else if (value instanceof Short s) {
|
||||
setShort(obj, s.shortValue());
|
||||
}
|
||||
else {
|
||||
throwSetIllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(Object obj, boolean z)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(z);
|
||||
}
|
||||
|
||||
public void setByte(Object obj, byte b)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
setShort(obj, b);
|
||||
}
|
||||
|
||||
public void setChar(Object obj, char c)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(c);
|
||||
}
|
||||
|
||||
public void setShort(Object obj, short s)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
if (isReadOnly()) {
|
||||
ensureObj(obj); // throw NPE if obj is null on instance field
|
||||
throwFinalFieldIllegalAccessException(s);
|
||||
}
|
||||
try {
|
||||
if (isStatic()) {
|
||||
setter.invokeExact(s);
|
||||
} else {
|
||||
setter.invokeExact(obj, s);
|
||||
}
|
||||
} catch (IllegalArgumentException|NullPointerException e) {
|
||||
throw e;
|
||||
} catch (ClassCastException e) {
|
||||
throw newSetIllegalArgumentException(obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setInt(Object obj, int i)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(i);
|
||||
}
|
||||
|
||||
public void setLong(Object obj, long l)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(l);
|
||||
}
|
||||
|
||||
public void setFloat(Object obj, float f)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(f);
|
||||
}
|
||||
|
||||
public void setDouble(Object obj, double d)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
throwSetIllegalArgumentException(d);
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ class NativeConstructorAccessorImpl extends ConstructorAccessorImpl {
|
||||
IllegalArgumentException,
|
||||
InvocationTargetException
|
||||
{
|
||||
// We can't inflate a constructor belonging to a vm-anonymous class
|
||||
// We can't inflate a constructor belonging to a hidden class
|
||||
// because that kind of class can't be referred to by name, hence can't
|
||||
// be found from the generated bytecode.
|
||||
if (++numInvocations > ReflectionFactory.inflationThreshold()
|
||||
|
@ -49,7 +49,7 @@ class NativeMethodAccessorImpl extends MethodAccessorImpl {
|
||||
public Object invoke(Object obj, Object[] args)
|
||||
throws IllegalArgumentException, InvocationTargetException
|
||||
{
|
||||
// We can't inflate methods belonging to vm-anonymous classes because
|
||||
// We can't inflate methods belonging to hidden classes because
|
||||
// that kind of class can't be referred to by name, hence can't be
|
||||
// found from the generated bytecode.
|
||||
if (++numInvocations > ReflectionFactory.inflationThreshold()
|
||||
|
@ -85,6 +85,15 @@ public class ReflectionFactory {
|
||||
private static boolean noInflation = false;
|
||||
private static int inflationThreshold = 15;
|
||||
|
||||
//
|
||||
// New implementation uses direct invocation of method handles
|
||||
private static final int METHOD_MH_ACCESSOR = 0x1;
|
||||
private static final int FIELD_MH_ACCESSOR = 0x2;
|
||||
private static final int ALL_MH_ACCESSORS = METHOD_MH_ACCESSOR|FIELD_MH_ACCESSOR;
|
||||
|
||||
private static int useDirectMethodHandle = ALL_MH_ACCESSORS;
|
||||
private static boolean useNativeAccessorOnly = false; // for testing only
|
||||
|
||||
// true if deserialization constructor checking is disabled
|
||||
private static boolean disableSerialConstructorChecks = false;
|
||||
|
||||
@ -137,24 +146,6 @@ public class ReflectionFactory {
|
||||
return soleInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an alternate reflective Method instance for the given method
|
||||
* intended for reflection to invoke, if present.
|
||||
*
|
||||
* A trusted method can define an alternate implementation for a method `foo`
|
||||
* by defining a method named "reflected$foo" that will be invoked
|
||||
* reflectively.
|
||||
*/
|
||||
private static Method findMethodForReflection(Method method) {
|
||||
String altName = "reflected$" + method.getName();
|
||||
try {
|
||||
return method.getDeclaringClass()
|
||||
.getDeclaredMethod(altName, method.getParameterTypes());
|
||||
} catch (NoSuchMethodException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Routines used by java.lang.reflect
|
||||
@ -181,37 +172,46 @@ public class ReflectionFactory {
|
||||
}
|
||||
boolean isFinal = Modifier.isFinal(field.getModifiers());
|
||||
boolean isReadOnly = isFinal && (!override || langReflectAccess.isTrustedFinalField(field));
|
||||
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 (useMethodHandleAccessor()) {
|
||||
return MethodHandleAccessorFactory.newMethodAccessor(method, callerSensitive);
|
||||
} else {
|
||||
if (noInflation && !method.getDeclaringClass().isHidden()) {
|
||||
return new MethodAccessorGenerator().
|
||||
generateMethod(method.getDeclaringClass(),
|
||||
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());
|
||||
} else {
|
||||
NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
|
||||
return acc.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
public ConstructorAccessor newConstructorAccessor(Constructor<?> c) {
|
||||
@ -232,11 +232,13 @@ public class ReflectionFactory {
|
||||
c = root;
|
||||
}
|
||||
|
||||
if (useMethodHandleAccessor()) {
|
||||
return MethodHandleAccessorFactory.newConstructorAccessor(c);
|
||||
} else {
|
||||
// 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)) {
|
||||
if (Reflection.isSubclassOf(declaringClass, ConstructorAccessorImpl.class)) {
|
||||
return new BootstrapConstructorAccessorImpl(c);
|
||||
}
|
||||
|
||||
@ -251,6 +253,7 @@ public class ReflectionFactory {
|
||||
return acc.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
@ -624,6 +627,22 @@ public class ReflectionFactory {
|
||||
return inflationThreshold;
|
||||
}
|
||||
|
||||
static boolean noInflation() {
|
||||
return noInflation;
|
||||
}
|
||||
|
||||
static boolean useMethodHandleAccessor() {
|
||||
return (useDirectMethodHandle & METHOD_MH_ACCESSOR) == METHOD_MH_ACCESSOR;
|
||||
}
|
||||
|
||||
static boolean useFieldHandleAccessor() {
|
||||
return (useDirectMethodHandle & FIELD_MH_ACCESSOR) == FIELD_MH_ACCESSOR;
|
||||
}
|
||||
|
||||
static boolean useNativeAccessorOnly() {
|
||||
return useNativeAccessorOnly;
|
||||
}
|
||||
|
||||
/** We have to defer full initialization of this class until after
|
||||
the static initializer is run since java.lang.reflect.Method's
|
||||
static initializer (more properly, that for
|
||||
@ -653,6 +672,20 @@ public class ReflectionFactory {
|
||||
throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e);
|
||||
}
|
||||
}
|
||||
val = props.getProperty("jdk.reflect.useDirectMethodHandle");
|
||||
if (val != null) {
|
||||
if (val.equals("false")) {
|
||||
useDirectMethodHandle = 0;
|
||||
} else if (val.equals("methods")) {
|
||||
useDirectMethodHandle = METHOD_MH_ACCESSOR;
|
||||
} else if (val.equals("fields")) {
|
||||
useDirectMethodHandle = FIELD_MH_ACCESSOR;
|
||||
}
|
||||
}
|
||||
val = props.getProperty("jdk.reflect.useNativeAccessorOnly");
|
||||
if (val != null && val.equals("true")) {
|
||||
useNativeAccessorOnly = true;
|
||||
}
|
||||
|
||||
disableSerialConstructorChecks =
|
||||
"true".equals(props.getProperty("jdk.disableSerialConstructorChecks"));
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,7 +28,6 @@ package jdk.internal.reflect;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
/** Base class for jdk.internal.misc.Unsafe-based FieldAccessors. The
|
||||
observation is that there are only nine types of fields from the
|
||||
@ -40,168 +39,16 @@ import jdk.internal.vm.annotation.Stable;
|
||||
abstract class UnsafeFieldAccessorImpl extends FieldAccessorImpl {
|
||||
static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
@Stable
|
||||
protected final long fieldOffset;
|
||||
protected final boolean isFinal;
|
||||
|
||||
UnsafeFieldAccessorImpl(Field field) {
|
||||
super(field);
|
||||
if (Modifier.isStatic(field.getModifiers()))
|
||||
int mods = field.getModifiers();
|
||||
this.isFinal = Modifier.isFinal(mods);
|
||||
if (Modifier.isStatic(mods))
|
||||
fieldOffset = unsafe.staticFieldOffset(field);
|
||||
else
|
||||
fieldOffset = unsafe.objectFieldOffset(field);
|
||||
isFinal = Modifier.isFinal(field.getModifiers());
|
||||
}
|
||||
|
||||
protected void ensureObj(Object o) {
|
||||
// NOTE: will throw NullPointerException, as specified, if o is null
|
||||
if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
|
||||
throwSetIllegalArgumentException(o);
|
||||
}
|
||||
}
|
||||
|
||||
private String getQualifiedFieldName() {
|
||||
return field.getDeclaringClass().getName() + "." +field.getName();
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetIllegalArgumentException(String type) {
|
||||
return new IllegalArgumentException(
|
||||
"Attempt to get "+field.getType().getName()+" field \"" +
|
||||
getQualifiedFieldName() + "\" with illegal data type conversion to "+type
|
||||
);
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(String attemptedType,
|
||||
String attemptedValue)
|
||||
throws IllegalAccessException {
|
||||
throw new IllegalAccessException(getSetMessage(attemptedType, attemptedValue));
|
||||
|
||||
}
|
||||
protected void throwFinalFieldIllegalAccessException(Object o) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException(o != null ? o.getClass().getName() : "", "");
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(boolean z) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("boolean", Boolean.toString(z));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(char b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("char", Character.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(byte b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("byte", Byte.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(short b) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("short", Short.toString(b));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(int i) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("int", Integer.toString(i));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(long i) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("long", Long.toString(i));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(float f) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("float", Float.toString(f));
|
||||
}
|
||||
|
||||
protected void throwFinalFieldIllegalAccessException(double f) throws IllegalAccessException {
|
||||
throwFinalFieldIllegalAccessException("double", Double.toString(f));
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetBooleanIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("boolean");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetByteIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("byte");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetCharIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("char");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetShortIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("short");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetIntIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("int");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetLongIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("long");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetFloatIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("float");
|
||||
}
|
||||
|
||||
protected IllegalArgumentException newGetDoubleIllegalArgumentException() {
|
||||
return newGetIllegalArgumentException("double");
|
||||
}
|
||||
|
||||
protected String getSetMessage(String attemptedType, String attemptedValue) {
|
||||
String err = "Can not set";
|
||||
if (Modifier.isStatic(field.getModifiers()))
|
||||
err += " static";
|
||||
if (isFinal)
|
||||
err += " final";
|
||||
err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " to ";
|
||||
if (!attemptedValue.isEmpty()) {
|
||||
err += "(" + attemptedType + ")" + attemptedValue;
|
||||
} else {
|
||||
if (!attemptedType.isEmpty())
|
||||
err += attemptedType;
|
||||
else
|
||||
err += "null value";
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(String attemptedType,
|
||||
String attemptedValue) {
|
||||
throw new IllegalArgumentException(getSetMessage(attemptedType,attemptedValue));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(Object o) {
|
||||
throwSetIllegalArgumentException(o != null ? o.getClass().getName() : "", "");
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(boolean b) {
|
||||
throwSetIllegalArgumentException("boolean", Boolean.toString(b));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(byte b) {
|
||||
throwSetIllegalArgumentException("byte", Byte.toString(b));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(char c) {
|
||||
throwSetIllegalArgumentException("char", Character.toString(c));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(short s) {
|
||||
throwSetIllegalArgumentException("short", Short.toString(s));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(int i) {
|
||||
throwSetIllegalArgumentException("int", Integer.toString(i));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(long l) {
|
||||
throwSetIllegalArgumentException("long", Long.toString(l));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(float f) {
|
||||
throwSetIllegalArgumentException("float", Float.toString(f));
|
||||
}
|
||||
|
||||
protected void throwSetIllegalArgumentException(double d) {
|
||||
throwSetIllegalArgumentException("double", Double.toString(d));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,13 +26,8 @@
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.security.AccessController;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
/** Base class for jdk.internal.misc.Unsafe-based FieldAccessors for static
|
||||
fields. The observation is that there are only nine types of
|
||||
fields from the standpoint of reflection code: the eight primitive
|
||||
@ -46,7 +41,6 @@ abstract class UnsafeStaticFieldAccessorImpl extends UnsafeFieldAccessorImpl {
|
||||
Set.of("base"));
|
||||
}
|
||||
|
||||
@Stable
|
||||
protected final Object base; // base
|
||||
|
||||
UnsafeStaticFieldAccessorImpl(Field field) {
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "jvm.h"
|
||||
#include "jdk_internal_reflect_NativeConstructorAccessorImpl.h"
|
||||
#include "jdk_internal_reflect_NativeMethodAccessorImpl.h"
|
||||
#include "jdk_internal_reflect_DirectMethodHandleAccessor_NativeAccessor.h"
|
||||
#include "jdk_internal_reflect_DirectConstructorHandleAccessor_NativeAccessor.h"
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_NativeMethodAccessorImpl_invoke0
|
||||
(JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)
|
||||
@ -38,3 +40,14 @@ JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_NativeConstructorAccessorImp
|
||||
{
|
||||
return JVM_NewInstanceFromConstructor(env, c, args);
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_DirectMethodHandleAccessor_00024NativeAccessor_invoke0
|
||||
(JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)
|
||||
{
|
||||
return JVM_InvokeMethod(env, m, obj, args);
|
||||
}
|
||||
JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_DirectConstructorHandleAccessor_00024NativeAccessor_newInstance0
|
||||
(JNIEnv *env, jclass unused, jobject c, jobjectArray args)
|
||||
{
|
||||
return JVM_NewInstanceFromConstructor(env, c, args);
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import java.util.function.Supplier;
|
||||
import jdk.internal.access.JavaUtilResourceBundleAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.CallerSensitiveAdapter;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import static jdk.internal.logger.DefaultLoggerFinder.isSystem;
|
||||
|
||||
@ -714,6 +715,7 @@ public class Logger {
|
||||
* #getLogger(java.lang.String)}.
|
||||
* @return a suitable Logger for {@code callerClass}.
|
||||
*/
|
||||
@CallerSensitiveAdapter
|
||||
private static Logger getLogger(String name, Class<?> callerClass) {
|
||||
return demandLogger(name, null, callerClass);
|
||||
}
|
||||
@ -788,6 +790,7 @@ public class Logger {
|
||||
* not {@code null}.
|
||||
* @return a suitable Logger for {@code callerClass}.
|
||||
*/
|
||||
@CallerSensitiveAdapter
|
||||
private static Logger getLogger(String name, String resourceBundleName,
|
||||
Class<?> callerClass) {
|
||||
Logger result = demandLogger(name, resourceBundleName, callerClass);
|
||||
|
@ -37,6 +37,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.CallerSensitiveAdapter;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
|
||||
@ -647,6 +648,7 @@ public class DriverManager {
|
||||
|
||||
|
||||
// Worker method called by the public getConnection() methods.
|
||||
@CallerSensitiveAdapter
|
||||
private static Connection getConnection(
|
||||
String url, java.util.Properties info, Class<?> caller) throws SQLException {
|
||||
/*
|
||||
|
@ -38,6 +38,12 @@
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
vmTestbase/nsk/jvmti/AttachOnDemand/attach002a/TestDescription.java 8265795 generic-all
|
||||
vmTestbase/nsk/jvmti/AttachOnDemand/attach022/TestDescription.java 8265795 generic-all
|
||||
vmTestbase/nsk/jdi/ObjectReference/referringObjects/referringObjects002/referringObjects002.java 8265796 generic-all
|
||||
|
||||
#############################################################################
|
||||
|
||||
# :hotspot_compiler
|
||||
|
||||
compiler/ciReplay/TestSAServer.java 8029528 generic-all
|
||||
|
@ -37,7 +37,7 @@ import jdk.test.lib.process.OutputAnalyzer;
|
||||
* @summary Test that various diagnostic commands which can show core reflection
|
||||
* invocation targets do so correctly (See: JDK-8203343).
|
||||
* @library /test/lib
|
||||
* @run testng/othervm -Dsun.reflect.noInflation=true ShowReflectionTargetTest
|
||||
* @run testng/othervm -Dsun.reflect.noInflation=true -Djdk.reflect.useDirectMethodHandle=false ShowReflectionTargetTest
|
||||
* @author stuefe
|
||||
*/
|
||||
|
||||
|
@ -2198,7 +2198,9 @@ class EADeoptFrameAfterReadLocalObject_02CTarget extends EATestCaseBaseTarget {
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
testMethodDepth = 8;
|
||||
// the method depth in debuggee is 11 as it includes all hidden frames
|
||||
// the expected method depth is 6 excluding 5 hidden frames
|
||||
testMethodDepth = 11-5;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -82,9 +82,7 @@ public class DumpStackTest {
|
||||
new CallFrame(DumpStackTest.class, "test"),
|
||||
new CallFrame(DumpStackTest.class, "main"),
|
||||
// if invoked from jtreg
|
||||
new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"), // non-public class
|
||||
new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
|
||||
new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
|
||||
new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"), // non-public class
|
||||
new CallFrame(Method.class, "invoke"),
|
||||
new CallFrame(Thread.class, "run"),
|
||||
};
|
||||
@ -138,9 +136,7 @@ public class DumpStackTest {
|
||||
new CallFrame(DumpStackTest.class, "testLambda"),
|
||||
new CallFrame(DumpStackTest.class, "main"),
|
||||
// if invoked from jtreg
|
||||
new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
|
||||
new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
|
||||
new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
|
||||
new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
|
||||
new CallFrame(Method.class, "invoke"),
|
||||
new CallFrame(Thread.class, "run")
|
||||
};
|
||||
@ -161,16 +157,12 @@ public class DumpStackTest {
|
||||
CallFrame[] callStack = new CallFrame[] {
|
||||
new CallFrame(Thread.class, "getStackTrace"),
|
||||
new CallFrame(DumpStackTest.class, "methodInvoke"),
|
||||
new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
|
||||
new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
|
||||
new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
|
||||
new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
|
||||
new CallFrame(Method.class, "invoke"),
|
||||
new CallFrame(DumpStackTest.class, "testMethodInvoke"),
|
||||
new CallFrame(DumpStackTest.class, "main"),
|
||||
// if invoked from jtreg
|
||||
new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
|
||||
new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
|
||||
new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
|
||||
new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
|
||||
new CallFrame(Method.class, "invoke"),
|
||||
new CallFrame(Thread.class, "run")
|
||||
};
|
||||
@ -196,9 +188,7 @@ public class DumpStackTest {
|
||||
new CallFrame(DumpStackTest.class, "testMethodHandle"),
|
||||
new CallFrame(DumpStackTest.class, "main"),
|
||||
// if invoked from jtreg
|
||||
new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
|
||||
new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
|
||||
new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
|
||||
new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
|
||||
new CallFrame(Method.class, "invoke"),
|
||||
new CallFrame(Thread.class, "run")
|
||||
};
|
||||
|
@ -25,12 +25,11 @@
|
||||
* @test
|
||||
* @bug 8157892 8157977
|
||||
* @summary Verify file name, line number and bci of native methods
|
||||
* @run main NativeMethod
|
||||
* @run main/othervm/native -Xcheck:jni NativeMethod
|
||||
*/
|
||||
|
||||
import java.lang.StackWalker.Option;
|
||||
import java.lang.StackWalker.StackFrame;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -44,29 +43,36 @@ public class NativeMethod {
|
||||
this.walker = StackWalker.getInstance(Option.SHOW_REFLECT_FRAMES);
|
||||
}
|
||||
|
||||
void test() throws Exception {
|
||||
// invoke via reflection that includes native methods
|
||||
Method m = NativeMethod.class.getDeclaredMethod("walk");
|
||||
m.invoke(this);
|
||||
}
|
||||
// this native method will invoke NativeMethod::walk method
|
||||
public native void test();
|
||||
|
||||
void walk() {
|
||||
public void walk() {
|
||||
List<StackFrame> nativeFrames = walker.walk(s ->
|
||||
s.filter(StackFrame::isNativeMethod)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
assertTrue(nativeFrames.size() > 0, "native frame not found");
|
||||
// find NativeMethod::test native frame
|
||||
nativeFrames.stream().filter(f -> f.isNativeMethod()
|
||||
&& f.getClassName().equals("NativeMethod")
|
||||
&& f.getMethodName().equals("test"))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("NativeMethod::test native method not found"));
|
||||
|
||||
for (StackFrame f : nativeFrames) {
|
||||
assertTrue(f.getFileName() != null, "source file expected to be found");
|
||||
assertTrue(f.getLineNumber() < 0, "line number expected to be unavailable");
|
||||
assertTrue(f.getByteCodeIndex() < 0, "bci expected to be unavailable");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void assertTrue(boolean value, String msg) {
|
||||
if (value != true)
|
||||
throw new AssertionError(msg);
|
||||
}
|
||||
|
||||
static {
|
||||
System.loadLibrary("nativeMethod");
|
||||
}
|
||||
}
|
||||
|
@ -100,15 +100,12 @@ public class VerifyStackTrace {
|
||||
"2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:147)\n" +
|
||||
"3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:160)\n" +
|
||||
"4: VerifyStackTrace.invoke(VerifyStackTrace.java:190)\n" +
|
||||
"5: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" +
|
||||
"6: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" +
|
||||
"7: java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" +
|
||||
"8: java.base/java.lang.reflect.Method.invoke(Method.java:520)\n" +
|
||||
"9: VerifyStackTrace$1.run(VerifyStackTrace.java:220)\n" +
|
||||
"10: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" +
|
||||
"11: VerifyStackTrace.test(VerifyStackTrace.java:229)\n" +
|
||||
"12: VerifyStackTrace.main(VerifyStackTrace.java:185)\n";
|
||||
|
||||
"5: java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:94)\n" +
|
||||
"6: java.base/java.lang.reflect.Method.invoke(Method.java:520)\n" +
|
||||
"7: VerifyStackTrace$1.run(VerifyStackTrace.java:220)\n" +
|
||||
"8: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" +
|
||||
"9: VerifyStackTrace.test(VerifyStackTrace.java:229)\n" +
|
||||
"10: VerifyStackTrace.main(VerifyStackTrace.java:185)\n";
|
||||
@Override public StackWalker walker() { return walker;}
|
||||
@Override public String description() { return description;}
|
||||
@Override public String expected() { return expected;}
|
||||
@ -130,22 +127,24 @@ public class VerifyStackTrace {
|
||||
// then you can cut & paste the <-- actual --> stack printed in the
|
||||
// test output in here (don't forget the final \n):
|
||||
private final String expected =
|
||||
"1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:213)\n" +
|
||||
"2: VerifyStackTrace$$Lambda$1/0x00000007c0089430.run(Unknown Source)\n" +
|
||||
"3: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:149)\n" +
|
||||
"4: java.base/java.lang.invoke.LambdaForm$DMH/0x00000007c008a830.invokeVirtual_LL_V(LambdaForm$DMH)\n" +
|
||||
"5: java.base/java.lang.invoke.LambdaForm$MH/0x00000007c008a830.invoke_MT(LambdaForm$MH)\n" +
|
||||
"6: VerifyStackTrace$Handle.run(VerifyStackTrace.java:162)\n" +
|
||||
"7: VerifyStackTrace.invoke(VerifyStackTrace.java:192)\n" +
|
||||
"8: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" +
|
||||
"9: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" +
|
||||
"10: java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" +
|
||||
"11: java.base/java.lang.reflect.Method.invoke(Method.java:520)\n" +
|
||||
"12: VerifyStackTrace$1.run(VerifyStackTrace.java:222)\n" +
|
||||
"13: java.base/java.security.AccessController.executePrivileged(AccessController.java:759)\n" +
|
||||
"14: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" +
|
||||
"15: VerifyStackTrace.test(VerifyStackTrace.java:231)\n" +
|
||||
"16: VerifyStackTrace.main(VerifyStackTrace.java:188)\n";
|
||||
"1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:280)\n" +
|
||||
"2: VerifyStackTrace$$Lambda$1/0x0000000801001848.run(Unknown Source)\n" +
|
||||
"3: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:206)\n" +
|
||||
"4: java.base/java.lang.invoke.DirectMethodHandle$Holder.invokeVirtual(DirectMethodHandle$Holder)\n" +
|
||||
"5: java.base/java.lang.invoke.LambdaForm$MH/0x0000000801004800.invoke_MT(LambdaForm$MH)\n" +
|
||||
"6: VerifyStackTrace$Handle.run(VerifyStackTrace.java:219)\n" +
|
||||
"7: VerifyStackTrace.invoke(VerifyStackTrace.java:259)\n" +
|
||||
"8: java.base/java.lang.invoke.LambdaForm$DMH/0x0000000801002000.invokeStatic(LambdaForm$DMH)\n" +
|
||||
"9: java.base/java.lang.invoke.LambdaForm$MH/0x0000000801003000.invoke(LambdaForm$MH)\n" +
|
||||
"10: java.base/java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder)\n" +
|
||||
"11: java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl(DirectMethodHandleAccessor.java:211)\n" +
|
||||
"12: java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:122)\n" +
|
||||
"13: java.base/java.lang.reflect.Method.invoke(Method.java:573)\n" +
|
||||
"14: VerifyStackTrace$1.run(VerifyStackTrace.java:292)\n" +
|
||||
"15: java.base/java.security.AccessController.executePrivileged(AccessController.java:753)\n" +
|
||||
"16: java.base/java.security.AccessController.doPrivileged(AccessController.java:312)\n" +
|
||||
"17: VerifyStackTrace.test(VerifyStackTrace.java:301)\n" +
|
||||
"18: VerifyStackTrace.main(VerifyStackTrace.java:254)\n";
|
||||
|
||||
@Override public StackWalker walker() { return walker;}
|
||||
@Override public String description() { return description;}
|
||||
|
59
test/jdk/java/lang/StackWalker/libnativeMethod.c
Normal file
59
test/jdk/java/lang/StackWalker/libnativeMethod.c
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
static jclass test_class;
|
||||
static jmethodID mid;
|
||||
static jint current_jni_version = JNI_VERSION_10;
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
JNIEnv *env;
|
||||
jclass cl;
|
||||
|
||||
(*vm)->GetEnv(vm, (void **) &env, current_jni_version);
|
||||
|
||||
cl = (*env)->FindClass(env, "NativeMethod");
|
||||
test_class = (*env)->NewGlobalRef(env, cl);
|
||||
mid = (*env)->GetMethodID(env, test_class, "walk", "()V");
|
||||
|
||||
return current_jni_version;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: NativeMethod
|
||||
* Method: test
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_NativeMethod_test(JNIEnv *env, jobject obj) {
|
||||
(*env)->CallVoidMethod(env, obj, mid);
|
||||
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
(*env)->ExceptionDescribe(env);
|
||||
(*env)->FatalError(env, "Exception thrown");
|
||||
}
|
||||
}
|
60
test/jdk/java/lang/invoke/CallerSensitiveMethodHandle.java
Normal file
60
test/jdk/java/lang/invoke/CallerSensitiveMethodHandle.java
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @run testng/othervm CallerSensitiveMethodHandle
|
||||
* @summary Check Lookup findVirtual, findStatic and unreflect behavior with
|
||||
* caller sensitive methods with focus on AccessibleObject.setAccessible
|
||||
*/
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static java.lang.invoke.MethodType.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class CallerSensitiveMethodHandle {
|
||||
private static int field = 0;
|
||||
@Test
|
||||
public void privateField() throws Throwable {
|
||||
Lookup l = MethodHandles.lookup();
|
||||
Field f = CallerSensitiveMethodHandle.class.getDeclaredField("field");
|
||||
MethodHandle mh = l.findVirtual(Field.class, "setInt", methodType(void.class, Object.class, int.class));
|
||||
int newValue = 5;
|
||||
mh.invokeExact(f, (Object) null, newValue);
|
||||
assertTrue(field == newValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupItself() throws Throwable {
|
||||
Lookup lookup = MethodHandles.lookup();
|
||||
MethodHandle MH_lookup2 = lookup.findStatic(MethodHandles.class, "lookup", methodType(Lookup.class));
|
||||
Lookup lookup2 = (Lookup) MH_lookup2.invokeExact();
|
||||
System.out.println(lookup2 + " original lookup class " + lookup.lookupClass());
|
||||
assertTrue(lookup2.lookupClass() == lookup.lookupClass());
|
||||
}
|
||||
}
|
74
test/jdk/java/lang/invoke/MethodHandleInvokeUOE.java
Normal file
74
test/jdk/java/lang/invoke/MethodHandleInvokeUOE.java
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @summary Test MethodHandle::invokeExact and MethodHandle::invoke throws
|
||||
* UnsupportedOperationException when called via Method::invoke
|
||||
* @run testng test.java.lang.invoke.MethodHandleInvokeUOE
|
||||
*/
|
||||
|
||||
package test.java.lang.invoke;
|
||||
|
||||
import org.testng.*;
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static java.lang.invoke.MethodType.*;
|
||||
|
||||
public class MethodHandleInvokeUOE {
|
||||
@Test
|
||||
public void testInvokeExact() throws Throwable {
|
||||
Class<?> clz = MethodHandle.class;
|
||||
String mname = "invokeExact";
|
||||
Method m = clz.getDeclaredMethod(mname, Object[].class);
|
||||
MethodHandle mh = MethodHandles.lookup().findVirtual(clz, mname, methodType(Object.class, Object[].class));
|
||||
try {
|
||||
m.invoke(mh, new Object[1]);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (!(e.getCause() instanceof UnsupportedOperationException)) {
|
||||
throw new RuntimeException("expected UnsupportedOperationException but got: "
|
||||
+ e.getCause().getClass().getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvoke() throws Throwable {
|
||||
Class<?> clz = MethodHandle.class;
|
||||
String mname = "invoke";
|
||||
Method m = clz.getDeclaredMethod(mname, Object[].class);
|
||||
MethodHandle mh = MethodHandles.lookup().findVirtual(clz, mname, methodType(Object.class, Object[].class));
|
||||
try {
|
||||
m.invoke(mh, new Object[1]);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (!(e.getCause() instanceof UnsupportedOperationException)) {
|
||||
throw new RuntimeException("expected UnsupportedOperationException but got: "
|
||||
+ e.getCause().getClass().getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 8196830 8235351
|
||||
* @bug 8196830 8235351 8257874
|
||||
* @modules java.base/jdk.internal.reflect
|
||||
* @run testng/othervm CallerSensitiveAccess
|
||||
* @summary Check Lookup findVirtual, findStatic and unreflect behavior with
|
||||
@ -355,6 +355,40 @@ public class CallerSensitiveAccess {
|
||||
mh.invoke(inaccessibleField(), true); // should throw ClassCastException
|
||||
}
|
||||
|
||||
/**
|
||||
* Field::getInt and Field::setInt are caller-sensitive methods.
|
||||
* Test access to private field.
|
||||
*/
|
||||
private static int privateField = 0;
|
||||
@Test
|
||||
public void testPrivateField() throws Throwable {
|
||||
Field f = CallerSensitiveAccess.class.getDeclaredField("privateField");
|
||||
// Field::setInt
|
||||
MethodType mtype = MethodType.methodType(void.class, Object.class, int.class);
|
||||
MethodHandle mh = MethodHandles.lookup().findVirtual(Field.class, "setInt", mtype);
|
||||
mh.invokeExact(f, (Object)null, 5);
|
||||
// Field::getInt
|
||||
mh = MethodHandles.lookup().findVirtual(Field.class, "getInt", MethodType.methodType(int.class, Object.class));
|
||||
int value = (int)mh.invokeExact(f, (Object)null);
|
||||
assertTrue(value == 5);
|
||||
}
|
||||
|
||||
private static class Inner {
|
||||
private static boolean privateField = false;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInnerPrivateField() throws Throwable {
|
||||
Field f = Inner.class.getDeclaredField("privateField");
|
||||
// Field::setInt
|
||||
MethodType mtype = MethodType.methodType(void.class, Object.class, boolean.class);
|
||||
MethodHandle mh = MethodHandles.lookup().findVirtual(Field.class, "setBoolean", mtype);
|
||||
mh.invokeExact(f, (Object)null, true);
|
||||
// Field::getInt
|
||||
mh = MethodHandles.lookup().findVirtual(Field.class, "getBoolean", MethodType.methodType(boolean.class, Object.class));
|
||||
boolean value = (boolean)mh.invokeExact(f, (Object)null);
|
||||
assertTrue(value);
|
||||
}
|
||||
|
||||
// -- supporting methods --
|
||||
|
36
test/jdk/java/lang/invoke/callerSensitive/Main.java
Normal file
36
test/jdk/java/lang/invoke/callerSensitive/Main.java
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8013527
|
||||
* @library src
|
||||
* @modules java.base/jdk.internal.reflect
|
||||
* @build java.base/java.util.CSM csm/*
|
||||
* @run main/othervm -Djava.security.manager=allow csm/jdk.test.MethodInvokeTest
|
||||
* @run main/othervm -Djava.security.manager=allow csm/jdk.test.MethodInvokeTest sm
|
||||
* @summary Test proper caller class is bound to method handle for caller-sensitive method
|
||||
*/
|
||||
public class Main {
|
||||
}
|
||||
|
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.test;
|
||||
|
||||
import java.lang.StackWalker.StackFrame;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.CSM;
|
||||
import java.security.Permission;
|
||||
import java.security.PermissionCollection;
|
||||
import java.security.Permissions;
|
||||
import java.security.Policy;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* This test invokes caller-sensitive methods via static reference,
|
||||
* reflection, MethodHandle, lambda. If there is no alternate implementation
|
||||
* of a CSM with a trailing caller class parameter, when a CSM is invoked
|
||||
* via method handle, an invoker class is injected as the caller class
|
||||
* which is defined by the same defining class loader, in the same runtime
|
||||
* package, and protection domain as the lookup class.
|
||||
*/
|
||||
public class MethodInvokeTest {
|
||||
static final Policy DEFAULT_POLICY = Policy.getPolicy();
|
||||
private static final String CALLER_METHOD = "caller";
|
||||
private static final String CALLER_NO_ALT_METHOD = "callerNoAlternateImpl";
|
||||
|
||||
public static void main(String... args) throws Throwable {
|
||||
boolean sm = args.length > 0 && args[0].equals("sm");
|
||||
System.err.format("Test %s security manager.%n",
|
||||
sm ? "with" : "without");
|
||||
if (sm) {
|
||||
setupSecurityManager();
|
||||
}
|
||||
|
||||
MethodInvokeTest test = new MethodInvokeTest();
|
||||
// test static call to java.util.CSM::caller
|
||||
test.staticMethodCall();
|
||||
// test java.lang.reflect.Method call
|
||||
test.reflectMethodCall();
|
||||
// test java.lang.invoke.MethodHandle
|
||||
test.invokeMethodHandle();
|
||||
// test method ref
|
||||
test.lambda();
|
||||
}
|
||||
|
||||
static void setupSecurityManager() {
|
||||
PermissionCollection perms = new Permissions();
|
||||
perms.add(new RuntimePermission("getStackWalkerWithClassReference"));
|
||||
Policy.setPolicy(new Policy() {
|
||||
@Override
|
||||
public boolean implies(ProtectionDomain domain, Permission p) {
|
||||
return perms.implies(p) || DEFAULT_POLICY.implies(domain, p);
|
||||
}
|
||||
});
|
||||
System.setSecurityManager(new SecurityManager());
|
||||
}
|
||||
|
||||
void staticMethodCall() {
|
||||
checkCaller(java.util.CSM.caller(), MethodInvokeTest.class, true);
|
||||
checkCaller(java.util.CSM.callerNoAlternateImpl(), MethodInvokeTest.class, false);
|
||||
}
|
||||
|
||||
void reflectMethodCall() throws Throwable {
|
||||
// zero-arg caller method
|
||||
checkCaller(Caller1.invoke(CSM.class.getMethod(CALLER_METHOD)), Caller1.class, true);
|
||||
checkCaller(Caller2.invoke(CSM.class.getMethod(CALLER_METHOD)), Caller2.class, true);
|
||||
// 4-arg caller method
|
||||
checkCaller(Caller1.invoke(CSM.class.getMethod(CALLER_METHOD, Object.class, Object.class, Object.class, Object.class),
|
||||
new Object[] { null, null, null, null}), Caller1.class, true);
|
||||
checkCaller(Caller2.invoke(CSM.class.getMethod(CALLER_METHOD, Object.class, Object.class, Object.class, Object.class),
|
||||
new Object[] { null, null, null, null}), Caller2.class, true);
|
||||
|
||||
// Reflection::getCallerClass will return the injected invoker class as the caller
|
||||
checkInjectedInvoker(Caller1.invoke(CSM.class.getMethod(CALLER_NO_ALT_METHOD)), Caller1.class);
|
||||
checkInjectedInvoker(Caller2.invoke(CSM.class.getMethod(CALLER_NO_ALT_METHOD)), Caller2.class);
|
||||
}
|
||||
|
||||
void invokeMethodHandle() throws Throwable {
|
||||
checkCaller(Caller1.invokeExact(CALLER_METHOD), Caller1.class, true);
|
||||
checkCaller(Caller2.invokeExact(CALLER_METHOD), Caller2.class, true);
|
||||
|
||||
checkInjectedInvoker(Caller1.invokeExact(CALLER_NO_ALT_METHOD), Caller1.class);
|
||||
checkInjectedInvoker(Caller2.invokeExact(CALLER_NO_ALT_METHOD), Caller2.class);
|
||||
}
|
||||
|
||||
void lambda() {
|
||||
CSM caller = LambdaTest.caller.get();
|
||||
LambdaTest.checkLambdaProxyClass(caller);
|
||||
|
||||
caller = LambdaTest.caller();
|
||||
LambdaTest.checkLambdaProxyClass(caller);
|
||||
}
|
||||
|
||||
static class Caller1 {
|
||||
static CSM invoke(Method csm) throws ReflectiveOperationException {
|
||||
return (CSM)csm.invoke(null);
|
||||
}
|
||||
static CSM invoke(Method csm, Object[] args) throws ReflectiveOperationException {
|
||||
return (CSM)csm.invoke(null, args);
|
||||
}
|
||||
static CSM invokeExact(String methodName) throws Throwable {
|
||||
MethodHandle mh = MethodHandles.lookup().findStatic(java.util.CSM.class,
|
||||
methodName, MethodType.methodType(CSM.class));
|
||||
return (CSM)mh.invokeExact();
|
||||
}
|
||||
}
|
||||
|
||||
static class Caller2 {
|
||||
static CSM invoke(Method csm) throws ReflectiveOperationException {
|
||||
return (CSM)csm.invoke(null);
|
||||
}
|
||||
static CSM invoke(Method csm, Object[] args) throws ReflectiveOperationException {
|
||||
return (CSM)csm.invoke(null, args);
|
||||
}
|
||||
static CSM invokeExact(String methodName) throws Throwable {
|
||||
MethodHandle mh = MethodHandles.lookup().findStatic(java.util.CSM.class,
|
||||
methodName, MethodType.methodType(CSM.class));
|
||||
return (CSM)mh.invokeExact();
|
||||
}
|
||||
}
|
||||
|
||||
static class LambdaTest {
|
||||
static Supplier<CSM> caller = java.util.CSM::caller;
|
||||
|
||||
static CSM caller() {
|
||||
return caller.get();
|
||||
}
|
||||
|
||||
/*
|
||||
* The class calling the caller-sensitive method is the lambda proxy class
|
||||
* generated for LambdaTest.
|
||||
*/
|
||||
static void checkLambdaProxyClass(CSM csm) {
|
||||
Class<?> caller = csm.caller;
|
||||
assertTrue(caller.isHidden(), caller + " should be a hidden class");
|
||||
assertEquals(caller.getModule(), LambdaTest.class.getModule());
|
||||
|
||||
int index = caller.getName().indexOf('/');
|
||||
String cn = caller.getName().substring(0, index);
|
||||
assertTrue(cn.startsWith(LambdaTest.class.getName() + "$$Lambda$"), caller + " should be a lambda proxy class");
|
||||
}
|
||||
}
|
||||
static void checkCaller(CSM csm, Class<?> expected, boolean adapter) {
|
||||
assertEquals(csm.caller, expected);
|
||||
assertEquals(csm.adapter, adapter);
|
||||
// verify no invoker class injected
|
||||
for (StackFrame frame : csm.stackFrames) {
|
||||
Class<?> c = frame.getDeclaringClass();
|
||||
if (c == expected) break;
|
||||
|
||||
if (c.getName().startsWith(expected.getName() + "$$InjectedInvoker"))
|
||||
throw new RuntimeException("should not have any invoker class injected");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The class calling the direct method handle of the caller-sensitive class
|
||||
* is the InjectedInvoker class generated for the given caller.
|
||||
*/
|
||||
static void checkInjectedInvoker(CSM csm, Class<?> expected) {
|
||||
Class<?> invoker = csm.caller;
|
||||
assertTrue(invoker.isHidden(), invoker + " should be a hidden class");
|
||||
assertEquals(invoker.getModule(), expected.getModule());
|
||||
assertEquals(csm.adapter, false);
|
||||
|
||||
int index = invoker.getName().indexOf('/');
|
||||
String cn = invoker.getName().substring(0, index);
|
||||
assertEquals(cn, expected.getName() + "$$InjectedInvoker");
|
||||
|
||||
// check the invoker class on the stack
|
||||
for (StackFrame frame : csm.stackFrames) {
|
||||
Class<?> c = frame.getDeclaringClass();
|
||||
if (c.getName().startsWith(expected.getName() + "$$InjectedInvoker"))
|
||||
break;
|
||||
|
||||
if (c == expected)
|
||||
throw new RuntimeException("no invoker class found before the expected caller class");
|
||||
}
|
||||
}
|
||||
|
||||
static void assertTrue(boolean value, String msg) {
|
||||
if (!value) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
static void assertEquals(Object o1, Object o2) {
|
||||
if (!o1.equals(o2)) {
|
||||
throw new RuntimeException(o1 + " != " + o2);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module csm {
|
||||
exports jdk.test;
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 java.util;
|
||||
|
||||
import java.lang.StackWalker.StackFrame;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.CallerSensitiveAdapter;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
import static java.lang.StackWalker.Option.*;
|
||||
|
||||
public class CSM {
|
||||
public final Class<?> caller;
|
||||
public final List<StackFrame> stackFrames;
|
||||
public final boolean adapter;
|
||||
CSM(Class<?> caller, boolean adapter) {
|
||||
this.caller = caller;
|
||||
StackWalker sw = StackWalker.getInstance(Set.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES));
|
||||
this.stackFrames = sw.walk(Stream::toList);
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the caller of this caller-sensitive method returned by
|
||||
* by Reflection::getCallerClass except if this method is called
|
||||
* via method handle.
|
||||
*/
|
||||
@CallerSensitive
|
||||
public static CSM caller() {
|
||||
return caller(Reflection.getCallerClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* If caller() is invoked via method handle, this alternate method is
|
||||
* called instead. The caller class would be the lookup class.
|
||||
*/
|
||||
@CallerSensitiveAdapter
|
||||
private static CSM caller(Class<?> caller) {
|
||||
return new CSM(caller, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the caller of this caller-sensitive method returned by
|
||||
* by Reflection::getCallerClass.
|
||||
*/
|
||||
@CallerSensitive
|
||||
public static CSM callerNoAlternateImpl() {
|
||||
return new CSM(Reflection.getCallerClass(), false);
|
||||
}
|
||||
|
||||
@CallerSensitive
|
||||
public static CSM caller(Object o1, Object o2, Object o3, Object o4) {
|
||||
return caller(o1, o2, o3, o4, Reflection.getCallerClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* If caller() is invoked via method handle, this alternate method is
|
||||
* called instead. The caller class would be the lookup class.
|
||||
*/
|
||||
@CallerSensitiveAdapter
|
||||
private static CSM caller(Object o1, Object o2, Object o3, Object o4, Class<?> caller) {
|
||||
return new CSM(caller, true);
|
||||
}
|
||||
}
|
137
test/jdk/java/lang/invoke/lookup/ChainedLookupTest.java
Normal file
137
test/jdk/java/lang/invoke/lookup/ChainedLookupTest.java
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8013527
|
||||
* @run testng/othervm ChainedLookupTest
|
||||
* @summary Test MethodHandles.lookup method to produce the Lookup object with
|
||||
* proper lookup class if invoked through reflection and method handle.
|
||||
*/
|
||||
|
||||
import java.lang.invoke.*;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.*;
|
||||
import static java.lang.invoke.MethodType.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class ChainedLookupTest {
|
||||
/**
|
||||
* Direct call to MethodHandles::lookup
|
||||
*/
|
||||
@Test
|
||||
public void staticLookup() throws Throwable {
|
||||
Lookup lookup = lookup();
|
||||
test(lookup, "Lookup produced via direct call to MethodHandles.lookup()");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflective Method::invoke on MethodHandles::lookup
|
||||
*/
|
||||
@Test
|
||||
public void methodInvoke() throws Throwable {
|
||||
Method m = MethodHandles.class.getMethod("lookup");
|
||||
Lookup lookup = (Lookup) m.invoke(null);
|
||||
test(lookup, "Lookup produced via reflective call");
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke Method::invoke on MethodHandles::lookup via MethodHandle
|
||||
*/
|
||||
@Test
|
||||
public void methodInvokeViaMethodHandle() throws Throwable {
|
||||
Method m = MethodHandles.class.getMethod("lookup");
|
||||
|
||||
MethodHandle mh = lookup().findVirtual(Method.class, "invoke",
|
||||
methodType(Object.class, Object.class, Object[].class));
|
||||
Lookup lookup = (Lookup)mh.invoke(m, (Object)null, (Object[])null);
|
||||
test(lookup, "Lookup produced via Method::invoke via MethodHandle");
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke MethodHandle on MethodHandles::lookup via Method::invoke
|
||||
*/
|
||||
// @Test
|
||||
public void methodHandleViaReflection() throws Throwable {
|
||||
MethodHandle MH_lookup = lookup().findStatic(MethodHandles.class, "lookup", methodType(Lookup.class));
|
||||
Lookup lookup = (Lookup) MH_lookup.invokeExact();
|
||||
|
||||
Method m = MethodHandle.class.getMethod("invoke", Object[].class);
|
||||
|
||||
// should this throw UnsupportedOperationException per MethodHandle::invoke spec?
|
||||
lookup = (Lookup)m.invoke(MH_lookup);
|
||||
test(lookup, "Lookup produced via MethodHandle::invoke via Method::invoke");
|
||||
}
|
||||
|
||||
/**
|
||||
* MethodHandle::invokeExact on MethodHandles::lookup
|
||||
*/
|
||||
@Test
|
||||
public void methodHandle() throws Throwable {
|
||||
MethodHandle MH_lookup = lookup().findStatic(MethodHandles.class, "lookup", methodType(Lookup.class));
|
||||
Lookup lookup = (Lookup) MH_lookup.invokeExact();
|
||||
test(lookup, "Lookup produced via MethodHandle");
|
||||
}
|
||||
|
||||
/**
|
||||
* MethodHandles::unreflect the reflective Method representing MethodHandles::lookup
|
||||
*/
|
||||
@Test
|
||||
public void unreflect() throws Throwable {
|
||||
MethodHandle MH_lookup = lookup().unreflect(MethodHandles.class.getMethod("lookup"));
|
||||
Lookup lookup = (Lookup) MH_lookup.invokeExact();
|
||||
test(lookup, "Lookup produced via unreflect MethodHandle");
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the Lookup object returned from MethodHandle::invokeExact on MethodHandles::lookup
|
||||
* to look up MethodHandle representing MethodHandles::lookup and then invoking it.
|
||||
*/
|
||||
@Test
|
||||
public void chainedMethodHandle() throws Throwable {
|
||||
MethodHandle MH_lookup = lookup().findStatic(MethodHandles.class, "lookup", methodType(Lookup.class));
|
||||
Lookup lookup = (Lookup) MH_lookup.invokeExact();
|
||||
MethodHandle MH_lookup2 = lookup.unreflect(MethodHandles.class.getMethod("lookup"));
|
||||
Lookup lookup2 = (Lookup) MH_lookup2.invokeExact();
|
||||
test(lookup2, "Lookup produced via a chained call to MethodHandle");
|
||||
}
|
||||
|
||||
void test(Lookup lookup, String msg) throws Throwable {
|
||||
assertTrue(lookup.lookupClass() == ChainedLookupTest.class);
|
||||
assertTrue(lookup.hasFullPrivilegeAccess());
|
||||
|
||||
MethodHandle mh = lookup.findStatic(lookup.lookupClass(), "say", methodType(void.class, String.class));
|
||||
mh.invokeExact(msg);
|
||||
}
|
||||
|
||||
private static void say(String msg) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
}
|
||||
|
@ -52,9 +52,7 @@ public class ReflectiveLookupTest {
|
||||
|
||||
Method lookupMethod = MethodHandles.class.getMethod("lookup");
|
||||
System.out.println("reflection method: " + lookupMethod);
|
||||
if (!lookupMethod.getName().equals("lookup")) {
|
||||
throw new RuntimeException("Unexpected name: " + lookupMethod.getName());
|
||||
}
|
||||
assertEquals(lookupMethod.getName(), "lookup");
|
||||
|
||||
// Get a full power Lookup reflectively.
|
||||
Lookup lookup2 = (Lookup) lookupMethod.invoke(null);
|
||||
|
@ -56,7 +56,7 @@ public class LookupTest {
|
||||
try {
|
||||
MethodHandles.class.getMethod("lookup").invoke(null);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (!(e.getCause() instanceof IllegalArgumentException)) {
|
||||
if (!(e.getCause() instanceof InternalError)) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
|
120
test/jdk/java/lang/reflect/ChainedReflection.java
Normal file
120
test/jdk/java/lang/reflect/ChainedReflection.java
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng/othervm ChainedReflection
|
||||
* @summary Test Method::invoke and Constructor::newInstance chained calls that
|
||||
* should wrap NPE in InvocationTargetException properly
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class ChainedReflection {
|
||||
public ChainedReflection() {}
|
||||
|
||||
ChainedReflection(Void dummy) throws ReflectiveOperationException {
|
||||
Method m = ChainedReflection.class.getMethod("throwNPE");
|
||||
try {
|
||||
m.invoke(null);
|
||||
} catch (InvocationTargetException e) {
|
||||
Throwable t = e.getTargetException();
|
||||
if (t instanceof NullPointerException npe) {
|
||||
throw npe;
|
||||
} else {
|
||||
throw new RuntimeException("Test failed (InvocationTargetException didn't wrap NullPointerException)");
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Test failed (Unexpected exception)", t);
|
||||
}
|
||||
}
|
||||
|
||||
public static void throwNPE() {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrap a NullPointerException with a new NullPointerException
|
||||
*/
|
||||
public static void npe() throws ReflectiveOperationException {
|
||||
NullPointerException cause;
|
||||
try {
|
||||
Optional.of(null);
|
||||
throw new RuntimeException("NPE not thrown");
|
||||
} catch (NullPointerException e) {
|
||||
cause = e;
|
||||
}
|
||||
Class<?> c = NullPointerException.class;
|
||||
Constructor<?> ctor = c.getConstructor();
|
||||
NullPointerException npe = (NullPointerException) ctor.newInstance();
|
||||
npe.initCause(cause);
|
||||
throw npe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the target method being invoked via core reflection that
|
||||
* throws NullPointerException allocated via Constructor::newInstance.
|
||||
* The newly allocated NPE thrown by the target method should be
|
||||
* wrapped by InvocationTargetException.
|
||||
*/
|
||||
@Test
|
||||
public void methodCallNewInstance() throws ReflectiveOperationException {
|
||||
Method m = ChainedReflection.class.getMethod("npe");
|
||||
try {
|
||||
m.invoke(null);
|
||||
} catch (InvocationTargetException e) {
|
||||
Throwable t = e.getTargetException();
|
||||
if (!(t instanceof NullPointerException)) {
|
||||
throw new RuntimeException("Test failed (InvocationTargetException didn't wrap NullPointerException)");
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Test failed (Unexpected exception)", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a constructor C calling the method "throwNPE" that throws
|
||||
* a new instance of NullPointerException.
|
||||
* C::newInstance should wrap NullPointerException thrown by
|
||||
* Method::invoke on "throwNPE" by InvocationTargetException.
|
||||
*/
|
||||
@Test
|
||||
public void ctorCallMethodInvoke() throws ReflectiveOperationException {
|
||||
Constructor<?> ctor = ChainedReflection.class.getDeclaredConstructor(Void.class);
|
||||
try {
|
||||
ctor.newInstance((Void)null);
|
||||
} catch (InvocationTargetException e) {
|
||||
Throwable t = e.getTargetException();
|
||||
if (!(t instanceof NullPointerException)) {
|
||||
throw new RuntimeException("Test failed (InvocationTargetException didn't wrap NullPointerException)");
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Test failed (Unexpected exception)", t);
|
||||
}
|
||||
}
|
||||
}
|
@ -87,25 +87,25 @@ public class TestFieldReflectValueOf {
|
||||
private volatile short shortVolatileField;
|
||||
|
||||
public static void main(String[] args) {
|
||||
testUnsafeStaticFieldAccessors();
|
||||
testUnsafeQualifiedStaticFieldAccessors();
|
||||
testUnsafeFieldAccessors();
|
||||
testUnsafeQualifiedFieldAccessors();
|
||||
testStaticFieldAccessors();
|
||||
testStaticVolatileFieldAccessors();
|
||||
testFieldAccessors();
|
||||
testVolatileFieldAccessors();
|
||||
}
|
||||
|
||||
private static void testUnsafeStaticFieldAccessors() {
|
||||
private static void testStaticFieldAccessors() {
|
||||
testFieldAccessors(true, false);
|
||||
}
|
||||
|
||||
private static void testUnsafeQualifiedStaticFieldAccessors() {
|
||||
private static void testStaticVolatileFieldAccessors() {
|
||||
testFieldAccessors(true, true);
|
||||
}
|
||||
|
||||
private static void testUnsafeFieldAccessors() {
|
||||
private static void testFieldAccessors() {
|
||||
testFieldAccessors(false, false);
|
||||
}
|
||||
|
||||
private static void testUnsafeQualifiedFieldAccessors() {
|
||||
private static void testVolatileFieldAccessors() {
|
||||
testFieldAccessors(false, true);
|
||||
}
|
||||
|
||||
|
106
test/jdk/java/lang/reflect/Method/MethodArityLimit.java
Normal file
106
test/jdk/java/lang/reflect/Method/MethodArityLimit.java
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8271820
|
||||
* @run testng/othervm MethodArityLimit
|
||||
* @summary Method exceeds the method handle arity limit (255).
|
||||
*/
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
public class MethodArityLimit {
|
||||
@Test
|
||||
public void testArityLimit() throws Throwable {
|
||||
Method m = this.getClass().getMethod("f", long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class, long.class, int.class);
|
||||
|
||||
long resultViaMethod = (long) m.invoke(null, 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L,
|
||||
8L, 9L, 10L, 11L, 12L, 13L, 14L, 15L, 16L, 17L, 18L, 19L, 20L, 21L, 22L,
|
||||
23L, 24L, 25L, 26L, 27L, 28L, 29L, 30L, 31L, 32L, 33L, 34L, 35L, 36L,
|
||||
37L, 38L, 39L, 40L, 41L, 42L, 43L, 44L, 45L, 46L, 47L, 48L, 49L, 50L,
|
||||
51L, 52L, 53L, 54L, 55L, 56L, 57L, 58L, 59L, 60L, 61L, 62L, 63L, 64L,
|
||||
65L, 66L, 67L, 68L, 69L, 70L, 71L, 72L, 73L, 74L, 75L, 76L, 77L, 78L,
|
||||
79L, 80L, 81L, 82L, 83L, 84L, 85L, 86L, 87L, 88L, 89L, 90L, 91L, 92L,
|
||||
93L, 94L, 95L, 96L, 97L, 98L, 99L, 100L, 101L, 102L, 103L, 104L, 105L,
|
||||
106L, 107L, 108L, 109L, 110L, 111L, 112L, 113L, 114L, 115L, 116L, 117L,
|
||||
118L, 119L, 120L, 121L, 122L, 123L, 124L, 125L, 126L, 127);
|
||||
|
||||
assertEquals(resultViaMethod, 127);
|
||||
|
||||
try {
|
||||
MethodHandle mh = MethodHandles.lookup().unreflect(m);
|
||||
fail("should fail in creating the method handle");
|
||||
} catch (IllegalArgumentException e) {}
|
||||
}
|
||||
|
||||
public static long f(long a0, long a1, long a2, long a3, long a4, long a5,
|
||||
long a6, long a7, long a8, long a9, long a10, long a11, long a12,
|
||||
long a13, long a14, long a15, long a16, long a17, long a18, long a19,
|
||||
long a20, long a21, long a22, long a23, long a24, long a25, long a26,
|
||||
long a27, long a28, long a29, long a30, long a31, long a32, long a33,
|
||||
long a34, long a35, long a36, long a37, long a38, long a39, long a40,
|
||||
long a41, long a42, long a43, long a44, long a45, long a46, long a47,
|
||||
long a48, long a49, long a50, long a51, long a52, long a53, long a54,
|
||||
long a55, long a56, long a57, long a58, long a59, long a60, long a61,
|
||||
long a62, long a63, long a64, long a65, long a66, long a67, long a68,
|
||||
long a69, long a70, long a71, long a72, long a73, long a74, long a75,
|
||||
long a76, long a77, long a78, long a79, long a80, long a81, long a82,
|
||||
long a83, long a84, long a85, long a86, long a87, long a88, long a89,
|
||||
long a90, long a91, long a92, long a93, long a94, long a95, long a96,
|
||||
long a97, long a98, long a99, long a100, long a101, long a102, long a103,
|
||||
long a104, long a105, long a106, long a107, long a108, long a109,
|
||||
long a110, long a111, long a112, long a113, long a114, long a115,
|
||||
long a116, long a117, long a118, long a119, long a120, long a121,
|
||||
long a122, long a123, long a124, long a125, long a126, int a127) {
|
||||
return a127;
|
||||
}
|
||||
}
|
639
test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java
Normal file
639
test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java
Normal file
@ -0,0 +1,639 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8271820
|
||||
* @modules java.base/jdk.internal.reflect
|
||||
* @summary Test compliance of ConstructorAccessor, FieldAccessor, MethodAccessor implementations
|
||||
* @run testng/othervm --add-exports java.base/jdk.internal.reflect=ALL-UNNAMED -Djdk.reflect.useDirectMethodHandle=true -XX:-ShowCodeDetailsInExceptionMessages MethodHandleAccessorsTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/jdk.internal.reflect
|
||||
* @run testng/othervm --add-exports java.base/jdk.internal.reflect=ALL-UNNAMED -Djdk.reflect.useDirectMethodHandle=false -XX:-ShowCodeDetailsInExceptionMessages MethodHandleAccessorsTest
|
||||
*/
|
||||
|
||||
|
||||
import jdk.internal.reflect.ConstructorAccessor;
|
||||
import jdk.internal.reflect.FieldAccessor;
|
||||
import jdk.internal.reflect.MethodAccessor;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.reflect.ReflectionFactory;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class MethodHandleAccessorsTest {
|
||||
|
||||
public static void public_static_V() {}
|
||||
|
||||
public static int public_static_I() { return 42; }
|
||||
|
||||
public void public_V() {}
|
||||
|
||||
public int public_I() { return 42; }
|
||||
|
||||
public static void public_static_I_V(int i) {}
|
||||
|
||||
public static int public_static_I_I(int i) { return i; }
|
||||
|
||||
public void public_I_V(int i) {}
|
||||
|
||||
public int public_I_I(int i) { return i; }
|
||||
|
||||
private static void private_static_V() {}
|
||||
|
||||
private static int private_static_I() { return 42; }
|
||||
|
||||
private void private_V() {}
|
||||
|
||||
private int private_I() { return 42; }
|
||||
|
||||
private static void private_static_I_V(int i) {}
|
||||
|
||||
private static int private_static_I_I(int i) { return i; }
|
||||
|
||||
private void private_I_V(int i) {}
|
||||
|
||||
private int private_I_I(int i) { return i; }
|
||||
|
||||
public static int varargs(int... values) {
|
||||
int sum = 0;
|
||||
for (int i : values) sum += i;
|
||||
return sum;
|
||||
|
||||
}
|
||||
public static int varargs_primitive(int first, int... rest) {
|
||||
int sum = first;
|
||||
if (rest != null) {
|
||||
sum *= 100;
|
||||
for (int i : rest) sum += i;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static String varargs_object(String first, String... rest) {
|
||||
StringBuilder sb = new StringBuilder(first);
|
||||
if (rest != null) {
|
||||
sb.append(Stream.of(rest).collect(Collectors.joining(",", "[", "]")));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static final class Public {
|
||||
public static final int STATIC_FINAL = 1;
|
||||
private final int i;
|
||||
private final String s;
|
||||
|
||||
public Public() {
|
||||
this.i = 0;
|
||||
this.s = null;
|
||||
}
|
||||
|
||||
public Public(int i) {
|
||||
this.i = i;
|
||||
this.s = null;
|
||||
}
|
||||
|
||||
public Public(String s) {
|
||||
this.i = 0;
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
public Public(int first, int... rest) {
|
||||
this(varargs_primitive(first, rest));
|
||||
}
|
||||
|
||||
public Public(String first, String... rest) {
|
||||
this(varargs_object(first, rest));
|
||||
}
|
||||
|
||||
public Public(RuntimeException exc) {
|
||||
throw exc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Public other = (Public) o;
|
||||
return i == other.i &&
|
||||
Objects.equals(s, other.s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(i, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Public{" +
|
||||
"i=" + i +
|
||||
", s='" + s + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
static final class Private {
|
||||
private final int i;
|
||||
|
||||
private Private() {
|
||||
this.i = 0;
|
||||
}
|
||||
|
||||
private Private(int i) {
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Private other = (Private) o;
|
||||
return i == other.i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Private{" +
|
||||
"i=" + i +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
static final class Thrower {
|
||||
public Thrower(RuntimeException exc) {
|
||||
throw exc;
|
||||
}
|
||||
public static void throws_exception(RuntimeException exc) {
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class Abstract {
|
||||
public Abstract() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if MethodAccessor::invoke implementation returns the expected
|
||||
* result or exceptions.
|
||||
*/
|
||||
static void doTestAccessor(Method m, MethodAccessor ma, Object target, Object[] args,
|
||||
Object expectedReturn, Throwable... expectedExceptions) {
|
||||
Object ret;
|
||||
Throwable exc;
|
||||
try {
|
||||
ret = ma.invoke(target, args);
|
||||
exc = null;
|
||||
} catch (Throwable e) {
|
||||
ret = null;
|
||||
exc = e;
|
||||
}
|
||||
System.out.println("\n" + m + ", invoked with target: " + target + ", args: " + Arrays.toString(args));
|
||||
|
||||
chechResult(ret, expectedReturn, exc, expectedExceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if ConstructorAccessor::newInstance implementation returns the
|
||||
* expected result or exceptions.
|
||||
*/
|
||||
static void doTestAccessor(Constructor c, ConstructorAccessor ca, Object[] args,
|
||||
Object expectedReturn, Throwable... expectedExceptions) {
|
||||
Object ret;
|
||||
Throwable exc;
|
||||
try {
|
||||
ret = ca.newInstance(args);
|
||||
exc = null;
|
||||
} catch (Throwable e) {
|
||||
ret = null;
|
||||
exc = e;
|
||||
}
|
||||
System.out.println("\n" + c + ", invoked with args: " + Arrays.toString(args));
|
||||
chechResult(ret, expectedReturn, exc, expectedExceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if FieldAccessor::get implementation returns the
|
||||
* expected result or exceptions.
|
||||
*/
|
||||
static void doTestAccessor(Field f, FieldAccessor fa, Object target,
|
||||
Object expectedValue, Throwable... expectedExceptions) {
|
||||
Object ret;
|
||||
Throwable exc;
|
||||
try {
|
||||
ret = fa.get(target);
|
||||
exc = null;
|
||||
} catch (Throwable e) {
|
||||
ret = null;
|
||||
exc = e;
|
||||
}
|
||||
System.out.println("\n" + f + ", invoked with target: " + target + ", value: " + ret);
|
||||
chechResult(ret, expectedValue, exc, expectedExceptions);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if FieldAccessor::set implementation returns the
|
||||
* expected result or exceptions.
|
||||
*/
|
||||
static void doTestAccessor(Field f, FieldAccessor fa, Object target, Object oldValue,
|
||||
Object newValue, Throwable... expectedExceptions) {
|
||||
Object ret;
|
||||
Throwable exc;
|
||||
try {
|
||||
fa.set(target, newValue);
|
||||
exc = null;
|
||||
ret = fa.get(target);
|
||||
} catch (Throwable e) {
|
||||
ret = null;
|
||||
exc = e;
|
||||
}
|
||||
System.out.println("\n" + f + ", invoked with target: " + target + ", value: " + ret);
|
||||
chechResult(ret, newValue, exc, expectedExceptions);
|
||||
}
|
||||
|
||||
static void chechResult(Object ret, Object expectedReturn, Throwable exc, Throwable... expectedExceptions) {
|
||||
if (exc != null) {
|
||||
checkException(exc, expectedExceptions);
|
||||
} else if (expectedExceptions.length > 0) {
|
||||
fail(exc, expectedExceptions);
|
||||
} else if (!Objects.equals(ret, expectedReturn)) {
|
||||
throw new AssertionError("Expected return:\n " + expectedReturn + "\ngot:\n " + ret);
|
||||
} else {
|
||||
System.out.println(" Got expected return: " + ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkException(Throwable exc, Throwable... expectedExceptions) {
|
||||
boolean match = false;
|
||||
for (Throwable expected : expectedExceptions) {
|
||||
if (exceptionMatches(exc, expected)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
System.out.println(" Got expected exception: " + exc);
|
||||
if (exc.getCause() != null) {
|
||||
System.out.println(" with cause: " + exc.getCause());
|
||||
}
|
||||
} else {
|
||||
fail(exc, expectedExceptions);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean exceptionMatches(Throwable exc, Throwable expected) {
|
||||
return expected.getClass().isInstance(exc) &&
|
||||
(Objects.equals(expected.getMessage(), exc.getMessage()) ||
|
||||
(exc.getMessage() != null && expected.getMessage() != null &&
|
||||
exc.getMessage().startsWith(expected.getMessage()))) &&
|
||||
(expected.getCause() == null || exceptionMatches(exc.getCause(), expected.getCause()));
|
||||
}
|
||||
|
||||
static void fail(Throwable thrownException, Throwable... expectedExceptions) {
|
||||
String msg;
|
||||
if (thrownException == null) {
|
||||
msg = "No exception thrown but there were expected exceptions (see suppressed)";
|
||||
} else if (expectedExceptions.length == 0) {
|
||||
msg = "Exception thrown (see cause) but there were no expected exceptions";
|
||||
} else {
|
||||
msg = "Exception thrown (see cause) but expected exceptions were different (see suppressed)";
|
||||
}
|
||||
AssertionError error = new AssertionError(msg, thrownException);
|
||||
Stream.of(expectedExceptions).forEach(error::addSuppressed);
|
||||
throw error;
|
||||
}
|
||||
|
||||
static void doTest(Method m, Object target, Object[] args, Object expectedReturn, Throwable... expectedException) {
|
||||
MethodAccessor ma = ReflectionFactory.getReflectionFactory().newMethodAccessor(m, Reflection.isCallerSensitive(m));
|
||||
try {
|
||||
doTestAccessor(m, ma, target, args, expectedReturn, expectedException);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(ma.getClass().getName() + " for method: " + m + " test failure", e);
|
||||
}
|
||||
}
|
||||
|
||||
static void doTest(Constructor c, Object[] args, Object expectedReturn, Throwable... expectedExceptions) {
|
||||
ConstructorAccessor ca = ReflectionFactory.getReflectionFactory().newConstructorAccessor(c);
|
||||
try {
|
||||
doTestAccessor(c, ca, args, expectedReturn, expectedExceptions);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(ca.getClass().getName() + " for constructor: " + c + " test failure", e);
|
||||
}
|
||||
}
|
||||
static void doTest(Field f, Object target, Object expectedValue, Throwable... expectedExceptions) {
|
||||
FieldAccessor fa = ReflectionFactory.getReflectionFactory().newFieldAccessor(f, false);
|
||||
try {
|
||||
doTestAccessor(f, fa, target, expectedValue, expectedExceptions);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(fa.getClass().getName() + " for field: " + f + " test failure", e);
|
||||
}
|
||||
}
|
||||
static void doTest(Field f, Object target, Object oldValue, Object newValue, Throwable... expectedExceptions) {
|
||||
FieldAccessor fa = ReflectionFactory.getReflectionFactory().newFieldAccessor(f, true);
|
||||
try {
|
||||
doTestAccessor(f, fa, target, oldValue, newValue, expectedExceptions);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(fa.getClass().getName() + " for field: " + f + " test failure", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Throwable[] noException = new Throwable[0];
|
||||
private static final Throwable[] mismatched_argument_type = new Throwable[] {
|
||||
new IllegalArgumentException("argument type mismatch")
|
||||
};
|
||||
private static final Throwable[] mismatched_target_type = new Throwable[] {
|
||||
new IllegalArgumentException("argument type mismatch"),
|
||||
new IllegalArgumentException("object is not an instance of declaring class"),
|
||||
};
|
||||
private static final Throwable[] cannot_get_final_field = new Throwable[] {
|
||||
new IllegalArgumentException("Can not get final")
|
||||
};
|
||||
private static final Throwable[] cannot_set_final_field = new Throwable[] {
|
||||
new IllegalArgumentException("Can not set final")
|
||||
};
|
||||
private static final Throwable[] wrong_argument_count_no_details = new Throwable[] {
|
||||
new IllegalArgumentException("wrong number of arguments")
|
||||
};
|
||||
private static final Throwable[] wrong_argument_count = new Throwable[] {
|
||||
new IllegalArgumentException("wrong number of arguments"),
|
||||
new IllegalArgumentException("array is not of length 1")
|
||||
};
|
||||
private static final Throwable[] null_argument = new Throwable[] {
|
||||
new IllegalArgumentException("wrong number of arguments"),
|
||||
new IllegalArgumentException("null array reference")
|
||||
};
|
||||
private static final Throwable[] null_argument_value = new Throwable[] {
|
||||
new IllegalArgumentException()
|
||||
};
|
||||
private static final Throwable[] null_argument_value_npe = new Throwable[] {
|
||||
new IllegalArgumentException("java.lang.NullPointerException"),
|
||||
new NullPointerException()
|
||||
};
|
||||
private static final Throwable[] null_target = new Throwable[] {
|
||||
new NullPointerException()
|
||||
};
|
||||
private static final Throwable[] wrapped_npe_no_msg = new Throwable[]{
|
||||
new InvocationTargetException(new NullPointerException())
|
||||
};
|
||||
private static final Throwable[] wrapped_npe = new Throwable[]{
|
||||
new InvocationTargetException(new NullPointerException("NPE"))
|
||||
};
|
||||
private static final Throwable[] wrapped_cce = new Throwable[]{
|
||||
new InvocationTargetException(new ClassCastException("CCE"))
|
||||
};
|
||||
private static final Throwable[] wrapped_iae = new Throwable[]{
|
||||
new InvocationTargetException(new IllegalArgumentException("IAE"))
|
||||
};
|
||||
|
||||
|
||||
@DataProvider(name = "testNoArgMethods")
|
||||
private Object[][] testNoArgMethods() {
|
||||
MethodHandleAccessorsTest inst = new MethodHandleAccessorsTest();
|
||||
Object[] emptyArgs = new Object[]{};
|
||||
return new Object[][] {
|
||||
new Object[] {"public_static_V", null, emptyArgs, null, noException},
|
||||
new Object[] {"public_static_V", null, null, null, noException},
|
||||
new Object[] {"public_static_I", null, emptyArgs, 42, noException},
|
||||
new Object[] {"public_static_I", null, null, 42, noException},
|
||||
new Object[] {"public_V", inst, emptyArgs, null, noException},
|
||||
new Object[] {"public_V", inst, null, null, noException},
|
||||
new Object[] {"public_I", inst, emptyArgs, 42, noException},
|
||||
new Object[] {"public_I", inst, null, 42, noException},
|
||||
new Object[] {"private_static_V", null, emptyArgs, null, noException},
|
||||
new Object[] {"private_static_I", null, emptyArgs, 42, noException},
|
||||
new Object[] {"private_V", inst, emptyArgs, null, noException},
|
||||
new Object[] {"private_I", inst, emptyArgs, 42, noException},
|
||||
new Object[] {"public_V", null, null, null, null_target},
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "testOneArgMethods")
|
||||
private Object[][] testOneArgMethods() {
|
||||
MethodHandleAccessorsTest inst = new MethodHandleAccessorsTest();
|
||||
Object wrongInst = new Object();
|
||||
boolean newImpl = Boolean.getBoolean("jdk.reflect.useDirectMethodHandle");
|
||||
return new Object[][]{
|
||||
new Object[] {"public_static_I_V", int.class, null, new Object[]{12}, null, noException},
|
||||
new Object[] {"public_static_I_I", int.class, null, new Object[]{12}, 12, noException},
|
||||
new Object[] {"public_I_V", int.class, inst, new Object[]{12}, null, noException},
|
||||
new Object[] {"public_I_I", int.class, inst, new Object[]{12}, 12, noException},
|
||||
new Object[] {"private_static_I_V", int.class, null, new Object[]{12}, null, noException},
|
||||
new Object[] {"private_static_I_I", int.class, null, new Object[]{12}, 12, noException},
|
||||
new Object[] {"private_I_V", int.class, inst, new Object[]{12}, null, noException},
|
||||
new Object[] {"private_I_I", int.class, inst, new Object[]{12}, 12, noException},
|
||||
|
||||
new Object[] {"public_static_I_I", int.class, null, new Object[]{"a"}, null, mismatched_argument_type},
|
||||
new Object[] {"public_I_I", int.class, inst, new Object[]{"a"}, null, mismatched_argument_type},
|
||||
new Object[] {"public_static_I_I", int.class, null, new Object[]{12, 13}, null,
|
||||
newImpl ? wrong_argument_count_no_details : wrong_argument_count},
|
||||
new Object[] {"public_I_I", int.class, inst, new Object[]{12, 13}, null,
|
||||
newImpl ? wrong_argument_count_no_details : wrong_argument_count},
|
||||
new Object[] {"public_I_I", int.class, wrongInst, new Object[]{12}, 12, mismatched_target_type},
|
||||
new Object[] {"public_I_I", int.class, null, new Object[]{12}, 12, null_target},
|
||||
|
||||
new Object[] {"public_static_I_V", int.class, null, null, null,
|
||||
newImpl ? wrong_argument_count_no_details : null_argument},
|
||||
new Object[] {"public_static_I_V", int.class, null, new Object[]{null}, null,
|
||||
newImpl ? null_argument_value_npe : null_argument_value},
|
||||
new Object[] {"public_I_I", int.class, inst, null, null, null_argument},
|
||||
|
||||
new Object[] {"public_I_I", int.class, inst, new Object[]{null}, null,
|
||||
newImpl ? null_argument_value_npe : null_argument_value},
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "testMethodsWithVarargs")
|
||||
private Object[][] testMethodsWithVarargs() {
|
||||
Class<?>[] paramTypes = new Class<?>[] { int[].class };
|
||||
Class<?>[] I_paramTypes = new Class<?>[] { int.class, int[].class };
|
||||
Class<?>[] L_paramTypes = new Class<?>[] { String.class, String[].class };
|
||||
return new Object[][]{
|
||||
new Object[] {"varargs", paramTypes, null, new Object[]{new int[]{1, 2, 3}}, 6, noException},
|
||||
new Object[] {"varargs", paramTypes, null, new Object[]{new int[]{}}, 0, noException},
|
||||
new Object[] {"varargs", paramTypes, null, new Object[]{null}, 0, wrapped_npe_no_msg},
|
||||
new Object[] {"varargs_primitive", I_paramTypes, null, new Object[]{1, new int[]{2, 3}}, 105, noException},
|
||||
new Object[] {"varargs_primitive", I_paramTypes, null, new Object[]{1, new int[]{}}, 100, noException},
|
||||
new Object[] {"varargs_primitive", I_paramTypes, null, new Object[]{1, null}, 1, noException},
|
||||
new Object[] {"varargs_object", L_paramTypes, null, new Object[]{"a", new String[]{"b", "c"}}, "a[b,c]", noException},
|
||||
new Object[] {"varargs_object", L_paramTypes, null, new Object[]{"a", new String[]{}}, "a[]", noException},
|
||||
new Object[] {"varargs_object", L_paramTypes, null, new Object[]{"a", null}, "a", noException},
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "testNoArgMethods")
|
||||
public void testNoArgMethod(String methodname, Object target, Object[] args,
|
||||
Object expectedReturn, Throwable[] expectedExpections) throws Exception {
|
||||
doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname), target, args, expectedReturn, expectedExpections);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "testOneArgMethods")
|
||||
public void testOneArgMethod(String methodname, Class<?> paramType, Object target, Object[] args,
|
||||
Object expectedReturn, Throwable[] expectedExpections) throws Exception {
|
||||
doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname, paramType), target, args, expectedReturn, expectedExpections);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "testMethodsWithVarargs")
|
||||
public void testMethodsWithVarargs(String methodname, Class<?>[] paramTypes, Object target, Object[] args,
|
||||
Object expectedReturn, Throwable[] expectedExpections) throws Exception {
|
||||
doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname, paramTypes), target, args, expectedReturn, expectedExpections);
|
||||
}
|
||||
|
||||
@DataProvider(name = "testConstructors")
|
||||
private Object[][] testConstructors() {
|
||||
return new Object[][]{
|
||||
new Object[]{null, new Object[]{}, new Public(), noException},
|
||||
new Object[]{null, null, new Public(), noException},
|
||||
new Object[]{new Class<?>[]{int.class}, new Object[]{12}, new Public(12), noException},
|
||||
new Object[]{new Class<?>[]{String.class}, new Object[]{"a"}, new Public("a"), noException},
|
||||
|
||||
|
||||
new Object[]{new Class<?>[]{int.class, int[].class}, new Object[]{1, new int[]{2, 3}}, new Public(105), noException},
|
||||
new Object[]{new Class<?>[]{int.class, int[].class}, new Object[]{1, new int[]{}}, new Public(100), noException},
|
||||
new Object[]{new Class<?>[]{int.class, int[].class}, new Object[]{1, null}, new Public(1), noException},
|
||||
|
||||
new Object[]{new Class<?>[]{String.class, String[].class}, new Object[]{"a", new String[]{"b", "c"}}, new Public("a[b,c]"), noException},
|
||||
new Object[]{new Class<?>[]{String.class, String[].class}, new Object[]{"a", new String[]{}}, new Public("a[]"), noException},
|
||||
new Object[]{new Class<?>[]{String.class, String[].class}, new Object[]{"a", null}, new Public("a"), noException},
|
||||
|
||||
// test ConstructorAccessor exceptions thrown
|
||||
new Object[]{new Class<?>[]{int.class}, new Object[]{"a"}, null, mismatched_argument_type},
|
||||
new Object[]{new Class<?>[]{int.class}, new Object[]{12, 13}, null, wrong_argument_count},
|
||||
new Object[]{new Class<?>[]{int.class}, null, null, null_argument},
|
||||
new Object[]{new Class<?>[]{RuntimeException.class}, new Object[]{new NullPointerException("NPE")}, null, wrapped_npe},
|
||||
new Object[]{new Class<?>[]{RuntimeException.class}, new Object[]{new IllegalArgumentException("IAE")}, null, wrapped_iae},
|
||||
new Object[]{new Class<?>[]{RuntimeException.class}, new Object[]{new ClassCastException("CCE")}, null, wrapped_cce},
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "testConstructors")
|
||||
public void testPublicConstructors(Class<?>[] paramTypes, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception {
|
||||
doTest(Public.class.getDeclaredConstructor(paramTypes), args, expectedReturn, expectedExpections);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOtherConstructors() throws Exception {
|
||||
doTest(Private.class.getDeclaredConstructor(), new Object[]{}, new Private());
|
||||
doTest(Private.class.getDeclaredConstructor(), null, new Private());
|
||||
doTest(Private.class.getDeclaredConstructor(int.class), new Object[]{12}, new Private(12));
|
||||
|
||||
doTest(Abstract.class.getDeclaredConstructor(), null, null, new InstantiationException());
|
||||
}
|
||||
|
||||
@DataProvider(name = "throwException")
|
||||
private Object[][] throwException() {
|
||||
return new Object[][]{
|
||||
new Object[] {new NullPointerException("NPE"), wrapped_npe},
|
||||
new Object[] {new IllegalArgumentException("IAE"), wrapped_iae},
|
||||
new Object[] {new ClassCastException("CCE"), wrapped_cce},
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Test Method::invoke and Constructor::newInstance to wrap NPE/CCE/IAE
|
||||
* thrown by the member
|
||||
*/
|
||||
@Test(dataProvider = "throwException")
|
||||
public void testInvocationTargetException(Throwable ex, Throwable[] expectedExpections) throws Exception {
|
||||
Object[] args = new Object[] { ex };
|
||||
// test static method
|
||||
doTest(Thrower.class.getDeclaredMethod("throws_exception", RuntimeException.class), null, args, null, expectedExpections);
|
||||
// test constructor
|
||||
doTest(Thrower.class.getDeclaredConstructor(RuntimeException.class), args, null, expectedExpections);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLambdaProxyClass() throws Exception {
|
||||
// test MethodAccessor on methods declared by hidden classes
|
||||
IntUnaryOperator intUnaryOp = i -> i;
|
||||
Method applyAsIntMethod = intUnaryOp.getClass().getDeclaredMethod("applyAsInt", int.class);
|
||||
doTest(applyAsIntMethod, intUnaryOp, new Object[]{12}, 12);
|
||||
}
|
||||
|
||||
@DataProvider(name = "readAccess")
|
||||
private Object[][] readAccess() {
|
||||
boolean newImpl = Boolean.getBoolean("jdk.reflect.useDirectMethodHandle");
|
||||
return new Object[][]{
|
||||
new Object[]{"i", new Public(100), 100, noException},
|
||||
new Object[]{"s", new Public("test"), "test", noException},
|
||||
new Object[]{"s", new Object(), "test",
|
||||
newImpl ? cannot_get_final_field : cannot_set_final_field},
|
||||
new Object[]{"s", null, "test", null_target},
|
||||
};
|
||||
}
|
||||
@DataProvider(name = "writeAccess")
|
||||
private Object[][] writeAccess() {
|
||||
return new Object[][]{
|
||||
new Object[]{"i", new Public(100), 100, 200, noException},
|
||||
new Object[]{"s", new Public("test"), "test", "newValue", noException},
|
||||
// ## no exception thrown
|
||||
// new Object[]{"i", new Public(100), 100, new Object(), cannot_set_final_field},
|
||||
new Object[]{"s", new Object(), "test", "dummy", cannot_set_final_field},
|
||||
new Object[]{"s", null, "test", "dummy", null_target},
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "readAccess")
|
||||
public void testFieldReadAccess(String name, Object target, Object expectedValue, Throwable[] expectedExpections) throws Exception {
|
||||
Field f = Public.class.getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
doTest(f, target, expectedValue, expectedExpections);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "writeAccess")
|
||||
public void testFieldWriteAccess(String name, Object target, Object oldValue, Object newValue, Throwable[] expectedExpections) throws Exception {
|
||||
Field f = Public.class.getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
doTest(f, target, oldValue, newValue, expectedExpections);
|
||||
}
|
||||
|
||||
// test static final field with read-only access
|
||||
@Test
|
||||
public void testStaticFinalFields() throws Exception {
|
||||
Field f = Public.class.getDeclaredField("STATIC_FINAL");
|
||||
doTest(f, new Public(), 1, noException);
|
||||
|
||||
try {
|
||||
f.setInt(null, 100);
|
||||
} catch (IllegalAccessException e) { }
|
||||
}
|
||||
}
|
115
test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java
Normal file
115
test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java
Normal 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8271820
|
||||
* @library /test/lib/
|
||||
* @modules jdk.compiler
|
||||
* @build CustomLoaderTest jdk.test.lib.compiler.CompilerUtils
|
||||
* @run testng/othervm CustomLoaderTest
|
||||
* @run testng/othervm -Dsun.reflect.noInflation=true CustomLoaderTest
|
||||
*
|
||||
* @summary Test method whose parameter types and return type are not visible to the caller.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class CustomLoaderTest {
|
||||
private static final Path CLASSES = Paths.get("classes");
|
||||
|
||||
@BeforeTest
|
||||
public void setup() throws IOException {
|
||||
String src = System.getProperty("test.src", ".");
|
||||
String classpath = System.getProperty("test.classes", ".");
|
||||
boolean rc = CompilerUtils.compile(Paths.get(src, "ReflectTest.java"), CLASSES, "-cp", classpath);
|
||||
if (!rc) {
|
||||
throw new RuntimeException("fail compilation");
|
||||
}
|
||||
try {
|
||||
Class<?> p = Class.forName("ReflectTest$P");
|
||||
fail("should not be visible to this loader");
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
TestLoader loader1 = new TestLoader();
|
||||
TestLoader loader2 = new TestLoader();
|
||||
Method m1 = loader1.findMethod();
|
||||
Method m2 = loader2.findMethod();
|
||||
|
||||
assertTrue(m1.getDeclaringClass() != m2.getDeclaringClass());
|
||||
|
||||
assertTrue(m1.getDeclaringClass() == loader1.c);
|
||||
assertTrue(m2.getDeclaringClass() == loader2.c);
|
||||
|
||||
Object o1 = m1.invoke(loader1.c.newInstance(), loader1.p.newInstance(), loader1.q.newInstance());
|
||||
Object o2 = m2.invoke(loader2.c.newInstance(), loader2.p.newInstance(), loader2.q.newInstance());
|
||||
|
||||
assertTrue(o1.getClass() != o2.getClass());
|
||||
assertTrue(o1.getClass() == loader1.r);
|
||||
assertTrue(o2.getClass() == loader2.r);
|
||||
}
|
||||
|
||||
static class TestLoader extends URLClassLoader {
|
||||
static URL[] toURLs() {
|
||||
try {
|
||||
return new URL[]{ CLASSES.toUri().toURL() };
|
||||
} catch (MalformedURLException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
static AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
final Class<?> c;
|
||||
final Class<?> p;
|
||||
final Class<?> q;
|
||||
final Class<?> r;
|
||||
TestLoader() throws ClassNotFoundException {
|
||||
super("testloader-" + counter.getAndIncrement(), toURLs(), ClassLoader.getPlatformClassLoader());
|
||||
this.c = Class.forName("ReflectTest", true, this);
|
||||
this.p = Class.forName("ReflectTest$P", true, this);
|
||||
this.q = Class.forName("ReflectTest$Q", true, this);
|
||||
this.r = Class.forName("ReflectTest$R", true, this);
|
||||
}
|
||||
|
||||
Method findMethod() throws ReflectiveOperationException {
|
||||
return c.getMethod("m", p, q);
|
||||
}
|
||||
}
|
||||
}
|
35
test/jdk/java/lang/reflect/callerCache/ReflectTest.java
Normal file
35
test/jdk/java/lang/reflect/callerCache/ReflectTest.java
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
public class ReflectTest {
|
||||
public static class P {
|
||||
}
|
||||
public static class Q {
|
||||
}
|
||||
public static class R {
|
||||
}
|
||||
|
||||
public R m(P p, Q q) {
|
||||
return new R();
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @build Initializer Test ExceptionInClassInitialization
|
||||
* @run main ExceptionInClassInitialization
|
||||
* @summary ensure InvocationTargetException thrown due to the initialization of
|
||||
* the declaring class wrapping with the proper cause
|
||||
*/
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class ExceptionInClassInitialization {
|
||||
public static void main(String... argv) throws ReflectiveOperationException {
|
||||
Class<?> c = Class.forName("Initializer");
|
||||
testExecMethod(c);
|
||||
testFieldAccess(c);
|
||||
}
|
||||
|
||||
static void testExecMethod(Class<?> cls) throws ReflectiveOperationException {
|
||||
try {
|
||||
cls.getDeclaredMethod("execMethod").invoke(null);
|
||||
throw new RuntimeException("InvocationTargetException not thrown");
|
||||
} catch (InvocationTargetException e) {
|
||||
// InvocationTargetException wraps the exception that was thrown while reflection.
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof ExceptionInInitializerError eiie) {
|
||||
if (eiie.getCause() instanceof MyException) {
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("ExceptionInInitializerError due to other exception than MyException!", eiie);
|
||||
}
|
||||
throw new RuntimeException("InvocationTargetException was thrown not due to error while initialization!", e);
|
||||
}
|
||||
}
|
||||
|
||||
static void testFieldAccess(Class<?> cls) throws ReflectiveOperationException {
|
||||
try {
|
||||
cls.getDeclaredMethod("fieldAccess").invoke(null);
|
||||
throw new RuntimeException("InvocationTargetException not thrown");
|
||||
} catch (InvocationTargetException e) {
|
||||
// the class initialization was run and failed. NoClassDefFoundError
|
||||
// should be thrown in this second attempt.
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof NoClassDefFoundError ncdfe) {
|
||||
t = t.getCause();
|
||||
if (t instanceof ExceptionInInitializerError eiie) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("InvocationTargetException was thrown not due to error while initialization!", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class Initializer {
|
||||
static void fieldAccess() throws Throwable {
|
||||
Class<?> testedClass = Class.forName("Test", false, Initializer.class.getClassLoader());
|
||||
testedClass.getDeclaredField("field").set(testedClass, 1);
|
||||
}
|
||||
|
||||
static void execMethod() throws Throwable {
|
||||
Class<?> testedClass = Class.forName("Test", false, Initializer.class.getClassLoader());
|
||||
testedClass.getDeclaredMethod("method").invoke(null);
|
||||
}
|
||||
}
|
45
test/jdk/java/lang/reflect/classInitialization/Test.java
Normal file
45
test/jdk/java/lang/reflect/classInitialization/Test.java
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class MyException extends RuntimeException {}
|
||||
|
||||
class SuperTest {
|
||||
static {
|
||||
if (true) {
|
||||
throw new MyException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Test extends SuperTest {
|
||||
|
||||
static int field = 0;
|
||||
|
||||
static void method() {};
|
||||
|
||||
static {
|
||||
if (true) {
|
||||
throw new OutOfMemoryError();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 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
|
||||
@ -22,6 +22,8 @@
|
||||
*/
|
||||
|
||||
import com.sun.tools.classfile.*;
|
||||
|
||||
import static com.sun.tools.classfile.AccessFlags.ACC_PRIVATE;
|
||||
import static com.sun.tools.classfile.ConstantPool.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -33,10 +35,13 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@ -70,6 +75,14 @@ public class CheckCSMs {
|
||||
"javax/sql/rowset/serial/SerialJavaObject#getFields ()[Ljava/lang/reflect/Field;"
|
||||
);
|
||||
|
||||
// These non-static non-final methods must not have @CallerSensitiveAdapter
|
||||
// methods that takes an additional caller class parameter.
|
||||
private static Set<String> UNSUPPORTED_VIRTUAL_METHODS =
|
||||
Set.of("java/io/ObjectStreamField#getType (Ljava/lang/Class;)Ljava/lang/Class;",
|
||||
"java/lang/Thread#getContextClassLoader (Ljava/lang/Class;)Ljava/lang/ClassLoader;",
|
||||
"javax/sql/rowset/serial/SerialJavaObject#getFields (Ljava/lang/Class;)[Ljava/lang/reflect/Field;"
|
||||
);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length > 0 && args[0].equals("--list")) {
|
||||
listCSMs = true;
|
||||
@ -84,9 +97,19 @@ public class CheckCSMs {
|
||||
result.stream().sorted()
|
||||
.collect(Collectors.joining("\n", "\n", "")));
|
||||
}
|
||||
|
||||
// check if all csm methods with a trailing Class parameter are supported
|
||||
checkCSMs.csmWithCallerParameter.values().stream()
|
||||
.flatMap(Set::stream)
|
||||
.forEach(m -> {
|
||||
if (UNSUPPORTED_VIRTUAL_METHODS.contains(m))
|
||||
throw new RuntimeException("Unsupported alternate csm adapter: " + m);
|
||||
});
|
||||
}
|
||||
|
||||
private final Set<String> nonFinalCSMs = new ConcurrentSkipListSet<>();
|
||||
private final Map<String, Set<String>> csmWithCallerParameter = new ConcurrentHashMap<>();
|
||||
|
||||
private final ReferenceFinder finder;
|
||||
public CheckCSMs() {
|
||||
this.finder = new ReferenceFinder(getFilter(), getVisitor());
|
||||
@ -129,9 +152,7 @@ public class CheckCSMs {
|
||||
m.getName(cf.constant_pool).equals("getCallerClass"))
|
||||
return;
|
||||
|
||||
String name = String.format("%s#%s %s", cf.getName(),
|
||||
m.getName(cf.constant_pool),
|
||||
m.descriptor.getValue(cf.constant_pool));
|
||||
String name = methodSignature(cf, m);
|
||||
if (!CheckCSMs.isStaticOrFinal(cf, m, cf.constant_pool)) {
|
||||
System.err.println("Unsupported @CallerSensitive: " + name);
|
||||
nonFinalCSMs.add(name);
|
||||
@ -140,6 +161,15 @@ public class CheckCSMs {
|
||||
System.out.format("@CS %s%n", name);
|
||||
}
|
||||
}
|
||||
|
||||
// find the adapter implementation for CSM with the caller parameter
|
||||
if (!csmWithCallerParameter.containsKey(cf.getName())) {
|
||||
Set<String> methods = Arrays.stream(cf.methods)
|
||||
.filter(m0 -> csmWithCallerParameter(cf, m, m0))
|
||||
.map(m0 -> methodSignature(cf, m0))
|
||||
.collect(Collectors.toSet());
|
||||
csmWithCallerParameter.put(cf.getName(), methods);
|
||||
}
|
||||
} catch (ConstantPoolException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
@ -147,8 +177,58 @@ public class CheckCSMs {
|
||||
};
|
||||
}
|
||||
|
||||
private static String methodSignature(ClassFile cf, Method m) {
|
||||
try {
|
||||
return String.format("%s#%s %s", cf.getName(),
|
||||
m.getName(cf.constant_pool),
|
||||
m.descriptor.getValue(cf.constant_pool));
|
||||
} catch (ConstantPoolException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean csmWithCallerParameter(ClassFile cf, Method csm, Method m) {
|
||||
ConstantPool cp = cf.constant_pool;
|
||||
try {
|
||||
int csmParamCount = csm.descriptor.getParameterCount(cp);
|
||||
int paramCount = m.descriptor.getParameterCount(cp);
|
||||
// an adapter method must have the same name and return type and a trailing Class parameter
|
||||
if (!(csm.getName(cp).equals(m.getName(cp)) &&
|
||||
paramCount == (csmParamCount+1) &&
|
||||
m.descriptor.getReturnType(cp).equals(csm.descriptor.getReturnType(cp)))) {
|
||||
return false;
|
||||
}
|
||||
// the descriptor of the adapter method must have the parameters
|
||||
// of the caller-sensitive method and an additional Class parameter
|
||||
String csmDesc = csm.descriptor.getParameterTypes(cp);
|
||||
String desc = m.descriptor.getParameterTypes(cp);
|
||||
int index = desc.indexOf(", java.lang.Class)");
|
||||
if (index == -1) {
|
||||
index = desc.indexOf("java.lang.Class)");
|
||||
if (index == -1) return false;
|
||||
}
|
||||
String s = desc.substring(0, index) + ")";
|
||||
if (s.equals(csmDesc)) {
|
||||
if (!m.access_flags.is(ACC_PRIVATE)) {
|
||||
throw new RuntimeException(methodSignature(cf, m) + " adapter method for " +
|
||||
methodSignature(cf, csm) + " must be private");
|
||||
}
|
||||
if (!isCallerSensitiveAdapter(m, cp)) {
|
||||
throw new RuntimeException(methodSignature(cf, m) + " adapter method for " +
|
||||
methodSignature(cf, csm) + " must be annotated with @CallerSensitiveAdapter");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} catch (ConstantPoolException|Descriptor.InvalidDescriptor e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final String CALLER_SENSITIVE_ANNOTATION
|
||||
= "Ljdk/internal/reflect/CallerSensitive;";
|
||||
private static final String CALLER_SENSITIVE_ADAPTER_ANNOTATION
|
||||
= "Ljdk/internal/reflect/CallerSensitiveAdapter;";
|
||||
|
||||
private static boolean isCallerSensitive(Method m, ConstantPool cp)
|
||||
throws ConstantPoolException
|
||||
@ -167,6 +247,23 @@ public class CheckCSMs {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isCallerSensitiveAdapter(Method m, ConstantPool cp)
|
||||
throws ConstantPoolException
|
||||
{
|
||||
RuntimeAnnotations_attribute attr =
|
||||
(RuntimeAnnotations_attribute)m.attributes.get(Attribute.RuntimeInvisibleAnnotations);
|
||||
if (attr != null) {
|
||||
for (int i = 0; i < attr.annotations.length; i++) {
|
||||
Annotation ann = attr.annotations[i];
|
||||
String annType = cp.getUTF8Value(ann.type_index);
|
||||
if (CALLER_SENSITIVE_ADAPTER_ANNOTATION.equals(annType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isStaticOrFinal(ClassFile cf, Method m, ConstantPool cp)
|
||||
throws ConstantPoolException
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 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
|
||||
@ -23,15 +23,69 @@
|
||||
|
||||
package boot;
|
||||
|
||||
import static java.lang.System.out;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class GetCallerClass {
|
||||
@jdk.internal.reflect.CallerSensitive
|
||||
public ClassLoader getCallerLoader() {
|
||||
Class<?> c = jdk.internal.reflect.Reflection.getCallerClass();
|
||||
return c.getClassLoader();
|
||||
|
||||
public Class<?> missingCallerSensitiveAnnotation() {
|
||||
return jdk.internal.reflect.Reflection.getCallerClass();
|
||||
}
|
||||
|
||||
public ClassLoader missingCallerSensitiveAnnotation() {
|
||||
Class<?> c = jdk.internal.reflect.Reflection.getCallerClass();
|
||||
return c.getClassLoader();
|
||||
@jdk.internal.reflect.CallerSensitive
|
||||
public Class<?> getCallerClass() {
|
||||
var caller = jdk.internal.reflect.Reflection.getCallerClass();
|
||||
out.println("caller: " + caller);
|
||||
out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
|
||||
return caller;
|
||||
}
|
||||
|
||||
private Class<?> getCallerClass(Class<?> caller) {
|
||||
out.println("caller: " + caller);
|
||||
out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
|
||||
return caller;
|
||||
}
|
||||
|
||||
@jdk.internal.reflect.CallerSensitive
|
||||
public static Class<?> getCallerClassStatic() {
|
||||
var caller = jdk.internal.reflect.Reflection.getCallerClass();
|
||||
out.println("caller: " + caller);
|
||||
out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
|
||||
return caller;
|
||||
}
|
||||
|
||||
private static Class<?> getCallerClassStatic(Class<?> caller) {
|
||||
out.println("caller: " + caller);
|
||||
out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
|
||||
return caller;
|
||||
}
|
||||
|
||||
@jdk.internal.reflect.CallerSensitive
|
||||
public Class<?> getCallerClassNoAlt() {
|
||||
var caller = jdk.internal.reflect.Reflection.getCallerClass();
|
||||
out.println("caller: " + caller);
|
||||
out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
|
||||
return caller;
|
||||
}
|
||||
|
||||
@jdk.internal.reflect.CallerSensitive
|
||||
public static Class<?> getCallerClassStaticNoAlt() {
|
||||
var caller = jdk.internal.reflect.Reflection.getCallerClass();
|
||||
out.println("caller: " + caller);
|
||||
out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
|
||||
return caller;
|
||||
}
|
||||
|
||||
private static Function<Stream<StackWalker.StackFrame>, String> toStackTrace() {
|
||||
return frames -> frames
|
||||
.takeWhile(
|
||||
frame -> !frame.getClassName().equals("GetCallerClassTest") ||
|
||||
!frame.getMethodName().equals("main"))
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.joining("\n ", " ", "\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 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,33 +25,78 @@
|
||||
* @test
|
||||
* @bug 8010117
|
||||
* @summary Test if the VM enforces Reflection.getCallerClass
|
||||
* be called by methods annotated with CallerSensitive
|
||||
* be called by system methods annotated with CallerSensitive plus
|
||||
* test reflective and method handle based invocation of caller-sensitive
|
||||
* methods with or without the CSM adapter method
|
||||
* @modules java.base/jdk.internal.reflect
|
||||
* @build SetupGetCallerClass boot.GetCallerClass
|
||||
* @run driver SetupGetCallerClass
|
||||
* @run main/othervm -Xbootclasspath/a:bcp GetCallerClassTest
|
||||
* @run main/othervm -Xbootclasspath/a:bcp -Djdk.reflect.useDirectMethodHandle=true GetCallerClassTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Verify the new NativeAccessor
|
||||
* @modules java.base/jdk.internal.reflect
|
||||
* @build SetupGetCallerClass boot.GetCallerClass
|
||||
* @run driver SetupGetCallerClass
|
||||
* @run main/othervm -Xbootclasspath/a:bcp -Djdk.reflect.useDirectMethodHandle=true -Djdk.reflect.useNativeAccessorOnly=true GetCallerClassTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Verify NativeMethodAccessorImpl
|
||||
* @modules java.base/jdk.internal.reflect
|
||||
* @build SetupGetCallerClass boot.GetCallerClass
|
||||
* @run driver SetupGetCallerClass
|
||||
* @run main/othervm -Xbootclasspath/a:bcp -Djdk.reflect.useDirectMethodHandle=false -Dsun.reflect.noInflation=false GetCallerClassTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Verify the old generated MethodAccessor
|
||||
* @modules java.base/jdk.internal.reflect
|
||||
* @build SetupGetCallerClass boot.GetCallerClass
|
||||
* @run driver SetupGetCallerClass
|
||||
* @run main/othervm -Xbootclasspath/a:bcp -Djdk.reflect.useDirectMethodHandle=false -Dsun.reflect.noInflation=true GetCallerClassTest
|
||||
*/
|
||||
|
||||
import boot.GetCallerClass;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.*;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
public class GetCallerClassTest {
|
||||
private final GetCallerClass gcc; // boot.GetCallerClass is in bootclasspath
|
||||
GetCallerClassTest() {
|
||||
this.gcc = new GetCallerClass();
|
||||
}
|
||||
// boot.GetCallerClass is in bootclasspath
|
||||
private static final Class<GetCallerClass> gccCl = GetCallerClass.class;
|
||||
private final GetCallerClass gcc = new GetCallerClass();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
GetCallerClassTest gcct = new GetCallerClassTest();
|
||||
// ensure methods are annotated with @CallerSensitive
|
||||
ensureAnnotationPresent(boot.GetCallerClass.class, "getCallerLoader", true);
|
||||
// ensure methods are annotated with @CallerSensitive and verify Reflection.isCallerSensitive()
|
||||
ensureAnnotationPresent(GetCallerClassTest.class, "testNonSystemMethod", false);
|
||||
// call Reflection.getCallerClass from bootclasspath with and without @CS
|
||||
gcct.testCallerSensitiveMethods();
|
||||
|
||||
ensureAnnotationPresent(gccCl, "getCallerClass", true);
|
||||
ensureAnnotationPresent(gccCl, "getCallerClassStatic", true);
|
||||
ensureAnnotationPresent(gccCl, "getCallerClassNoAlt", true);
|
||||
ensureAnnotationPresent(gccCl, "getCallerClassStaticNoAlt", true);
|
||||
|
||||
// call Reflection.getCallerClass from bootclasspath without @CS
|
||||
gcct.testMissingCallerSensitiveAnnotation();
|
||||
// call Reflection.getCallerClass from classpath with @CS
|
||||
gcct.testNonSystemMethod();
|
||||
// call Reflection.getCallerClass from bootclasspath with @CS
|
||||
gcct.testCallerSensitiveMethods();
|
||||
// call @CS methods using reflection
|
||||
gcct.testCallerSensitiveMethodsUsingReflection();
|
||||
// call @CS methods using method handles
|
||||
gcct.testCallerSensitiveMethodsUsingMethodHandles();
|
||||
// call @CS methods using reflection but call Method.invoke with a method handle
|
||||
gcct.testCallerSensitiveMethodsUsingMethodHandlesAndReflection();
|
||||
}
|
||||
|
||||
private static void ensureAnnotationPresent(Class<?> c, String name, boolean cs)
|
||||
@ -67,58 +112,170 @@ public class GetCallerClassTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void testCallerSensitiveMethods() {
|
||||
private void testMissingCallerSensitiveAnnotation() {
|
||||
System.out.println("\ntestMissingCallerSensitiveAnnotation...");
|
||||
try {
|
||||
ClassLoader cl = gcc.getCallerLoader();
|
||||
if (cl != GetCallerClassTest.class.getClassLoader()) {
|
||||
throw new RuntimeException("mismatched class loader");
|
||||
}
|
||||
gcc.missingCallerSensitiveAnnotation();
|
||||
throw new RuntimeException("getCallerLoader not marked with @CallerSensitive");
|
||||
throw new RuntimeException("shouldn't have succeeded");
|
||||
} catch (InternalError e) {
|
||||
StackTraceElement[] stackTrace = e.getStackTrace();
|
||||
checkStackTrace(stackTrace, e);
|
||||
if (!stackTrace[1].getClassName().equals(GetCallerClass.class.getName()) ||
|
||||
!stackTrace[1].getMethodName().equals("missingCallerSensitiveAnnotation")) {
|
||||
throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
|
||||
}
|
||||
if (!stackTrace[2].getClassName().equals(GetCallerClassTest.class.getName()) ||
|
||||
!stackTrace[2].getMethodName().equals("testCallerSensitiveMethods")) {
|
||||
throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
|
||||
}
|
||||
if (e.getMessage().startsWith("CallerSensitive annotation expected")) {
|
||||
System.out.println("Expected error: " + e.getMessage());
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CallerSensitive
|
||||
private void testNonSystemMethod() {
|
||||
System.out.println("\ntestNonSystemMethod...");
|
||||
try {
|
||||
Class<?> c = Reflection.getCallerClass();
|
||||
throw new RuntimeException("@CallerSensitive testNonSystemMethods not supported");
|
||||
throw new RuntimeException("shouldn't have succeeded");
|
||||
} catch (InternalError e) {
|
||||
StackTraceElement[] stackTrace = e.getStackTrace();
|
||||
checkStackTrace(stackTrace, e);
|
||||
if (!stackTrace[1].getClassName().equals(GetCallerClassTest.class.getName()) ||
|
||||
!stackTrace[1].getMethodName().equals("testNonSystemMethod")) {
|
||||
throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
|
||||
}
|
||||
if (!stackTrace[2].getClassName().equals(GetCallerClassTest.class.getName()) ||
|
||||
!stackTrace[2].getMethodName().equals("main")) {
|
||||
throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
|
||||
}
|
||||
if (e.getMessage().startsWith("CallerSensitive annotation expected")) {
|
||||
System.out.println("Expected error: " + e.getMessage());
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStackTrace(StackTraceElement[] stackTrace, Error e) {
|
||||
if (stackTrace.length < 3) {
|
||||
throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
|
||||
private void testCallerSensitiveMethods() {
|
||||
System.out.println();
|
||||
Class<?> caller;
|
||||
|
||||
caller = gcc.getCallerClass();
|
||||
if (caller != GetCallerClassTest.class) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
|
||||
if (!stackTrace[0].getClassName().equals("jdk.internal.reflect.Reflection") ||
|
||||
!stackTrace[0].getMethodName().equals("getCallerClass")) {
|
||||
throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
|
||||
caller = GetCallerClass.getCallerClassStatic();
|
||||
if (caller != GetCallerClassTest.class) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
}
|
||||
|
||||
private void testCallerSensitiveMethodsUsingReflection() {
|
||||
System.out.println();
|
||||
|
||||
try {
|
||||
Class<?> caller;
|
||||
|
||||
caller = (Class<?>) gccCl.getDeclaredMethod("getCallerClass").invoke(gcc);
|
||||
if (caller != GetCallerClassTest.class) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
|
||||
caller = (Class<?>) gccCl.getDeclaredMethod("getCallerClassStatic").invoke(null);
|
||||
if (caller != GetCallerClassTest.class) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
|
||||
caller = (Class<?>) gccCl.getDeclaredMethod("getCallerClassNoAlt").invoke(gcc);
|
||||
if (!caller.isNestmateOf(GetCallerClassTest.class)) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
|
||||
caller = (Class<?>) gccCl.getDeclaredMethod("getCallerClassStaticNoAlt").invoke(null);
|
||||
if (!caller.isNestmateOf(GetCallerClassTest.class)) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
} catch (ReflectiveOperationException|SecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void testCallerSensitiveMethodsUsingMethodHandles() {
|
||||
System.out.println();
|
||||
|
||||
try {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
MethodType mt = MethodType.methodType(Class.class);
|
||||
Class<?> caller;
|
||||
|
||||
caller = (Class<?>) lookup.findVirtual(gccCl, "getCallerClass", mt).invokeExact(gcc);
|
||||
if (caller != GetCallerClassTest.class) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
|
||||
caller = (Class<?>) lookup.findStatic(gccCl, "getCallerClassStatic", mt).invokeExact();
|
||||
if (caller != GetCallerClassTest.class) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
|
||||
caller = (Class<?>) lookup.findVirtual(gccCl, "getCallerClassNoAlt", mt).invokeExact(gcc);
|
||||
if (!caller.isNestmateOf(GetCallerClassTest.class)) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
|
||||
caller = (Class<?>) lookup.findStatic(gccCl, "getCallerClassStaticNoAlt", mt).invokeExact();
|
||||
if (!caller.isNestmateOf(GetCallerClassTest.class)) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
} catch (RuntimeException | Error e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Object[] EMPTY_ARRAY = new Object[0];
|
||||
|
||||
private void testCallerSensitiveMethodsUsingMethodHandlesAndReflection() {
|
||||
// In the old implementation, the caller returned is java.lang.invoke.Method
|
||||
// since it looks up the caller through stack walking.
|
||||
// The new implementation uses the special calling sequence and Method::invoke
|
||||
// defines an adapter method such that the stack walking is done only once
|
||||
// using the same caller class.
|
||||
String s = System.getProperty("jdk.reflect.useDirectMethodHandle", "true");
|
||||
boolean newImpl = Boolean.valueOf(s);
|
||||
Class<?> expectedCaller = newImpl ? GetCallerClassTest.class : Method.class;
|
||||
|
||||
System.out.println();
|
||||
try {
|
||||
MethodHandle methodInvokeMh = MethodHandles
|
||||
.lookup()
|
||||
.findVirtual(Method.class, "invoke", MethodType.methodType(Object.class, Object.class, Object[].class));
|
||||
|
||||
Class<?> caller;
|
||||
|
||||
caller = (Class<?>) methodInvokeMh.invoke(gccCl.getDeclaredMethod("getCallerClass"), gcc, EMPTY_ARRAY);
|
||||
if (caller != expectedCaller) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
|
||||
caller = (Class<?>) methodInvokeMh.invoke(gccCl.getDeclaredMethod("getCallerClassStatic"), null, EMPTY_ARRAY);
|
||||
if (caller != expectedCaller) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
|
||||
caller = (Class<?>) methodInvokeMh.invoke(gccCl.getDeclaredMethod("getCallerClassNoAlt"), gcc, EMPTY_ARRAY);
|
||||
if (newImpl) {
|
||||
if (!caller.isNestmateOf(GetCallerClassTest.class)) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
} else {
|
||||
if (caller != expectedCaller) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
}
|
||||
|
||||
caller = (Class<?>) methodInvokeMh.invoke(gccCl.getDeclaredMethod("getCallerClassStaticNoAlt"), null, EMPTY_ARRAY);
|
||||
if (newImpl) {
|
||||
if (!caller.isNestmateOf(GetCallerClassTest.class)) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
} else {
|
||||
if (caller != expectedCaller) {
|
||||
throw new RuntimeException("mismatched caller: " + caller);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException | Error e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,9 @@ public final class TestHiddenMethod {
|
||||
System.out.println("visibleEvent:" + visibleEvent);
|
||||
|
||||
assertTrue(hasHiddenStackFrame(hiddenEvent), "No hidden frame in hidden event: " + hiddenEvent);
|
||||
assertFalse(hasHiddenStackFrame(visibleEvent), "Hidden frame in visible event: " + visibleEvent);
|
||||
|
||||
// Temporary disable this test until JDK-8272064 is resolved.
|
||||
// assertFalse(hasHiddenStackFrame(visibleEvent), "Hidden frame in visible event: " + visibleEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,6 +245,7 @@ public class ExceptionsTest extends KullaTesting {
|
||||
new ExceptionInfo(ExceptionInInitializerError.class, null,
|
||||
new StackTraceElement("java.lang.Class", "forName0", "Class.java", -2),
|
||||
new StackTraceElement("java.lang.Class", "forName", "Class.java", -2),
|
||||
new StackTraceElement("java.lang.Class", "forName", "Class.java", -2),
|
||||
newStackTraceElement("", "", se.snippet(), 1)));
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -42,7 +42,7 @@ import java.util.concurrent.TimeUnit;
|
||||
@BenchmarkMode(Mode.SingleShotTime)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
@Fork(value = 30, warmups = 10)
|
||||
@Fork(value = 10, warmups = 0)
|
||||
public class ReflectionColdstartBenchmark {
|
||||
|
||||
static class Nested {
|
||||
@ -121,9 +121,22 @@ public class ReflectionColdstartBenchmark {
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void invokeMethods(Blackhole bh) throws ReflectiveOperationException {
|
||||
public void invokeMethods() throws ReflectiveOperationException {
|
||||
// As this is testing warmup, JITs are unlikely to progress to the
|
||||
// point where the lack of a blackhole might lead to DCE. Omitting it
|
||||
// makes it easier to test this code minimal use of JMH code
|
||||
for (Method m : methods) {
|
||||
bh.consume(m.invoke(null, arg));
|
||||
m.invoke(null, arg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs invokeMethods once without any JMH interaction, acting as an
|
||||
* independent startup benchmark.
|
||||
*/
|
||||
public static void main(String ... args) throws Exception {
|
||||
var coldstart = new ReflectionColdstartBenchmark();
|
||||
coldstart.setup();
|
||||
coldstart.invokeMethods();
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,13 @@ package org.openjdk.bench.java.lang.reflect;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.CompilerControl;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
@ -43,9 +45,9 @@ import java.util.concurrent.TimeUnit;
|
||||
/**
|
||||
* Benchmark measuring field access and method invocation using different conditions:
|
||||
* <ul>
|
||||
* <li>Const - Method/Field is constant-foldable</li>
|
||||
* <li>Var - Method/Field is single-instance but not constant-foldable</li>
|
||||
* <li>Poly - multiple Method/Field instances used at single call-site</li>
|
||||
* <li>Const - Constructor/Method/Field is constant-foldable</li>
|
||||
* <li>Var - Constructor/Method/Field is single-instance but not constant-foldable</li>
|
||||
* <li>Poly - multiple Constructor/Method/Field instances used at single call-site</li>
|
||||
* </ul>
|
||||
*/
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@ -78,8 +80,8 @@ public class ReflectionSpeedBenchmark {
|
||||
|
||||
static {
|
||||
try {
|
||||
staticMethodConst = staticMethodVar = ReflectionSpeedBenchmark.class.getDeclaredMethod("sumStatic", int.class, int.class);
|
||||
instanceMethodConst = instanceMethodVar = ReflectionSpeedBenchmark.class.getDeclaredMethod("sumInstance", int.class, int.class);
|
||||
staticMethodConst = staticMethodVar = ReflectionSpeedBenchmark.class.getDeclaredMethod("sumStatic", Integer.class, Integer.class);
|
||||
instanceMethodConst = instanceMethodVar = ReflectionSpeedBenchmark.class.getDeclaredMethod("sumInstance", Integer.class, Integer.class);
|
||||
|
||||
staticFieldConst = staticFieldVar = ReflectionSpeedBenchmark.class.getDeclaredField("staticField");
|
||||
instanceFieldConst = instanceFieldVar = ReflectionSpeedBenchmark.class.getDeclaredField("instanceField");
|
||||
@ -268,7 +270,7 @@ public class ReflectionSpeedBenchmark {
|
||||
}
|
||||
|
||||
private int rnd = 0;
|
||||
private int a, b;
|
||||
private Integer a, b;
|
||||
private Object o;
|
||||
private NestedInstance instance;
|
||||
|
||||
@ -276,58 +278,126 @@ public class ReflectionSpeedBenchmark {
|
||||
return rnd += 7;
|
||||
}
|
||||
|
||||
// @Param({"true", "false"})
|
||||
private boolean polluteProfile = true;
|
||||
|
||||
@Setup(Level.Iteration)
|
||||
public void setup() {
|
||||
a = nextRnd();
|
||||
b = nextRnd();
|
||||
o = new Object();
|
||||
instance = new NestedInstance();
|
||||
|
||||
if (polluteProfile) {
|
||||
try {
|
||||
Constructor ctor = ReflectionSpeedBenchmark.class.getDeclaredConstructor(Integer.class);
|
||||
Method test1 = ReflectionSpeedBenchmark.class.getDeclaredMethod("test1", Object.class);
|
||||
Method test2 = ReflectionSpeedBenchmark.class.getDeclaredMethod("test2", Object.class, Object.class);
|
||||
Field f = ReflectionSpeedBenchmark.class.getDeclaredField("testField");
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
invokeHelper2(staticMethodVar, null, a, b);
|
||||
invokeHelper2(instanceMethodVar, this, a, b);
|
||||
invokeHelper2(test2, null, a, b);
|
||||
invokeHelper1(staticMethodsPoly[i & (staticMethodsPoly.length - 1)], instance, o);
|
||||
invokeHelper1(instanceMethodsPoly[i & (instanceMethodsPoly.length - 1)], instance, o);
|
||||
invokeHelper1(test1, null, a);
|
||||
|
||||
newInstanceHelper(constructorVar, constructorArgs);
|
||||
int index = i & (constructorsPoly.length - 1);
|
||||
newInstanceHelper(constructorsPoly[index], constructorsArgsPoly[index]);
|
||||
newInstanceHelper(ctor, new Object[]{a});
|
||||
|
||||
getIntHelper(staticFieldVar, null);
|
||||
getIntHelper(instanceFieldVar, this);
|
||||
getDoubleHelper(f, null);
|
||||
getHelper(staticFieldsPoly[i & (staticFieldsPoly.length - 1)], null);
|
||||
getHelper(instanceFieldsPoly[i & (instanceFieldsPoly.length - 1)], instance);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int sumStatic(int a, int b) {
|
||||
return a + b;
|
||||
public static Integer sumStatic(Integer a, Integer b) {
|
||||
return a; // a + b;
|
||||
}
|
||||
|
||||
public int sumInstance(int a, int b) {
|
||||
return a + b;
|
||||
public Integer sumInstance(Integer a, Integer b) {
|
||||
return a; // a + b;
|
||||
}
|
||||
|
||||
public static int staticField;
|
||||
public int instanceField;
|
||||
public ReflectionSpeedBenchmark() {}
|
||||
|
||||
// used for polluting the profile
|
||||
private ReflectionSpeedBenchmark(Integer a) {}
|
||||
static void test1(Object a) {}
|
||||
static void test2(Object a, Object b) {}
|
||||
static double testField;
|
||||
|
||||
// methods
|
||||
@CompilerControl(CompilerControl.Mode.INLINE)
|
||||
static Object invokeHelper1(Method m, Object recv, Object arg1) throws InvocationTargetException, IllegalAccessException {
|
||||
return m.invoke(recv, arg1);
|
||||
}
|
||||
|
||||
@CompilerControl(CompilerControl.Mode.INLINE)
|
||||
static Object invokeHelper2(Method m, Object recv, Object arg1, Object arg2) throws InvocationTargetException, IllegalAccessException {
|
||||
return m.invoke(recv, arg1, arg2);
|
||||
}
|
||||
|
||||
@CompilerControl(CompilerControl.Mode.INLINE)
|
||||
static Object newInstanceHelper(Constructor ctor, Object[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException {
|
||||
return ctor.newInstance(args);
|
||||
}
|
||||
|
||||
@CompilerControl(CompilerControl.Mode.INLINE)
|
||||
static int getIntHelper(Field f, Object recv) throws IllegalAccessException {
|
||||
return f.getInt(recv);
|
||||
}
|
||||
@CompilerControl(CompilerControl.Mode.INLINE)
|
||||
static double getDoubleHelper(Field f, Object recv) throws IllegalAccessException {
|
||||
return f.getDouble(recv);
|
||||
}
|
||||
|
||||
@CompilerControl(CompilerControl.Mode.INLINE)
|
||||
static Object getHelper(Field f, Object recv) throws IllegalAccessException {
|
||||
return f.get(recv);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int staticMethodConst() {
|
||||
public Object staticMethodConst() {
|
||||
try {
|
||||
return (Integer) staticMethodConst.invoke(null, a, b);
|
||||
return invokeHelper2(staticMethodConst, null, a, b);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int instanceMethodConst() {
|
||||
public Object instanceMethodConst() {
|
||||
try {
|
||||
return (Integer) instanceMethodConst.invoke(this, a, b);
|
||||
return invokeHelper2(instanceMethodConst, this, a, b);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int staticMethodVar() {
|
||||
public Object staticMethodVar() {
|
||||
try {
|
||||
return (Integer) staticMethodVar.invoke(null, a, b);
|
||||
return invokeHelper2(staticMethodVar, null, a, b);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int instanceMethodVar() {
|
||||
public Object instanceMethodVar() {
|
||||
try {
|
||||
return (Integer) instanceMethodVar.invoke(this, a, b);
|
||||
return invokeHelper2(instanceMethodVar, this, a, b);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -336,7 +406,7 @@ public class ReflectionSpeedBenchmark {
|
||||
@Benchmark
|
||||
public Object staticMethodPoly() {
|
||||
try {
|
||||
return staticMethodsPoly[nextRnd() & (staticMethodsPoly.length - 1)].invoke(null, o);
|
||||
return invokeHelper1(staticMethodsPoly[nextRnd() & (staticMethodsPoly.length - 1)], null, o);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -345,7 +415,7 @@ public class ReflectionSpeedBenchmark {
|
||||
@Benchmark
|
||||
public Object instanceMethodPoly() {
|
||||
try {
|
||||
return instanceMethodsPoly[nextRnd() & (instanceMethodsPoly.length - 1)].invoke(instance, o);
|
||||
return invokeHelper1(instanceMethodsPoly[nextRnd() & (instanceMethodsPoly.length - 1)], instance, o);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -356,7 +426,7 @@ public class ReflectionSpeedBenchmark {
|
||||
@Benchmark
|
||||
public int staticFieldConst() {
|
||||
try {
|
||||
return staticFieldConst.getInt(null);
|
||||
return getIntHelper(staticFieldConst, null);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -365,7 +435,7 @@ public class ReflectionSpeedBenchmark {
|
||||
@Benchmark
|
||||
public int instanceFieldConst() {
|
||||
try {
|
||||
return instanceFieldConst.getInt(this);
|
||||
return getIntHelper(instanceFieldConst, this);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -374,7 +444,7 @@ public class ReflectionSpeedBenchmark {
|
||||
@Benchmark
|
||||
public int staticFieldVar() {
|
||||
try {
|
||||
return staticFieldVar.getInt(null);
|
||||
return getIntHelper(staticFieldVar, null);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -383,7 +453,7 @@ public class ReflectionSpeedBenchmark {
|
||||
@Benchmark
|
||||
public int instanceFieldVar() {
|
||||
try {
|
||||
return instanceFieldVar.getInt(this);
|
||||
return getIntHelper(instanceFieldVar, this);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -392,7 +462,7 @@ public class ReflectionSpeedBenchmark {
|
||||
@Benchmark
|
||||
public Object staticFieldPoly() {
|
||||
try {
|
||||
return staticFieldsPoly[nextRnd() & (staticFieldsPoly.length - 1)].get(null);
|
||||
return getHelper(staticFieldsPoly[nextRnd() & (staticFieldsPoly.length - 1)], null);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -401,7 +471,7 @@ public class ReflectionSpeedBenchmark {
|
||||
@Benchmark
|
||||
public Object instanceFieldPoly() {
|
||||
try {
|
||||
return instanceFieldsPoly[nextRnd() & (instanceFieldsPoly.length - 1)].get(instance);
|
||||
return getHelper(instanceFieldsPoly[nextRnd() & (instanceFieldsPoly.length - 1)], instance);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -412,7 +482,7 @@ public class ReflectionSpeedBenchmark {
|
||||
@Benchmark
|
||||
public Object constructorConst() {
|
||||
try {
|
||||
return constructorConst.newInstance(constructorArgs);
|
||||
return newInstanceHelper(constructorConst, constructorArgs);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -421,7 +491,7 @@ public class ReflectionSpeedBenchmark {
|
||||
@Benchmark
|
||||
public Object constructorVar() {
|
||||
try {
|
||||
return constructorVar.newInstance(constructorArgs);
|
||||
return newInstanceHelper(constructorVar, constructorArgs);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -431,7 +501,7 @@ public class ReflectionSpeedBenchmark {
|
||||
public Object constructorPoly() {
|
||||
try {
|
||||
int i = nextRnd() & (constructorsPoly.length - 1);
|
||||
return constructorsPoly[i].newInstance(constructorsArgsPoly[i]);
|
||||
return newInstanceHelper(constructorsPoly[i], constructorsArgsPoly[i]);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user