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

8013527: calling MethodHandles.lookup on itself leads to errors

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

View File

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

View File

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

View File

@ -73,6 +73,7 @@ import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.misc.Unsafe;
import jdk.internal.module.Resources;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.CallerSensitiveAdapter;
import jdk.internal.reflect.ConstantPool;
import jdk.internal.reflect.Reflection;
import jdk.internal.reflect.ReflectionFactory;
@ -372,9 +373,15 @@ public final class Class<T> implements java.io.Serializable,
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
return forName(className, caller);
}
// Caller-sensitive adapter method for reflective invocation
@CallerSensitiveAdapter
private static Class<?> forName(String className, Class<?> caller)
throws ClassNotFoundException {
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
/**
* Returns the {@code Class} object associated with the class or
@ -456,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

View File

@ -64,6 +64,7 @@ import jdk.internal.perf.PerfCounter;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.CallerSensitiveAdapter;
import jdk.internal.reflect.Reflection;
import jdk.internal.util.StaticProperty;
import sun.reflect.misc.ReflectUtil;
@ -1615,9 +1616,13 @@ public abstract class ClassLoader {
*/
@CallerSensitive
protected static boolean registerAsParallelCapable() {
Class<? extends ClassLoader> callerClass =
Reflection.getCallerClass().asSubclass(ClassLoader.class);
return ParallelLoaders.register(callerClass);
return registerAsParallelCapable(Reflection.getCallerClass());
}
// Caller-sensitive adapter method for reflective invocation
@CallerSensitiveAdapter
private static boolean registerAsParallelCapable(Class<?> caller) {
return ParallelLoaders.register(caller.asSubclass(ClassLoader.class));
}
/**

View File

@ -26,7 +26,6 @@
package java.lang.invoke;
import jdk.internal.misc.CDS;
import jdk.internal.misc.VM;
import jdk.internal.org.objectweb.asm.*;
import sun.invoke.util.BytecodeDescriptor;
import sun.invoke.util.VerifyAccess;
@ -37,7 +36,6 @@ import java.io.FilePermission;
import java.io.Serializable;
import java.lang.constant.ConstantDescs;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
@ -46,8 +44,10 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.PropertyPermission;
import java.util.Set;
import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
import static java.lang.invoke.MethodType.methodType;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
/**
@ -57,7 +57,6 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
* @see LambdaMetafactory
*/
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
private static final int CLASSFILE_VERSION = VM.classFileVersion();
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
private static final String NAME_CTOR = "<init>";
@ -106,7 +105,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
// condy to load implMethod from class data
MethodType classDataMType = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
MethodType classDataMType = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
Handle classDataBsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData",
classDataMType.descriptorString(), false);
implMethodCondy = new ConstantDynamic(ConstantDescs.DEFAULT_NAME, MethodHandle.class.descriptorString(), classDataBsm);
@ -227,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);
}
}
}

View File

@ -44,6 +44,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import static java.lang.invoke.LambdaForm.BasicType;
@ -316,7 +317,7 @@ class InvokerBytecodeGenerator {
* Extract the MemberName of a newly-defined method.
*/
private MemberName loadMethod(byte[] classFile) {
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile)
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile, Set.of())
.defineClass(true, classDataValues());
return resolveInvokerMember(invokerClass, invokerName, invokerType);
}
@ -376,7 +377,7 @@ class InvokerBytecodeGenerator {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
mv.visitLdcInsn(Type.getType("L" + className + ";"));
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandleNatives",
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandles",
"classData", "(Ljava/lang/Class;)Ljava/lang/Object;", false);
// we should optimize one single element case that does not need to create a List
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/util/List");

View File

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

View File

@ -25,8 +25,7 @@
package java.lang.invoke;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM;
import jdk.internal.ref.CleanerFactory;
import sun.invoke.util.Wrapper;
@ -35,7 +34,6 @@ import java.lang.reflect.Field;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
@ -248,6 +246,7 @@ class MethodHandleNatives {
return true;
}
static {
VM.setJavaLangInvokeInited();
assert(verifyConstants());
}
@ -665,8 +664,7 @@ class MethodHandleNatives {
static boolean canBeCalledVirtual(MemberName mem) {
assert(mem.isInvocable());
return mem.getName().equals("getContextClassLoader") &&
canBeCalledVirtual(mem, java.lang.Thread.class);
return mem.getName().equals("getContextClassLoader") && canBeCalledVirtual(mem, java.lang.Thread.class);
}
static boolean canBeCalledVirtual(MemberName symbolicRef, Class<?> definingClass) {
@ -676,16 +674,4 @@ class MethodHandleNatives {
return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef
symbolicRefClass.isInterface()); // Mdef implements Msym
}
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
/*
* Returns the class data set by the VM in the Class::classData field.
*
* This is also invoked by LambdaForms as it cannot use condy via
* MethodHandles.classData due to bootstrapping issue.
*/
static Object classData(Class<?> c) {
UNSAFE.ensureClassInitialized(c);
return JLA.classData(c);
}
}

View File

@ -32,6 +32,7 @@ import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.CallerSensitiveAdapter;
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.ForceInline;
import sun.invoke.util.ValueConversions;
@ -63,7 +64,9 @@ import java.util.stream.Stream;
import static java.lang.invoke.LambdaForm.BasicType.V_TYPE;
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
import static java.lang.invoke.MethodHandleStatics.newInternalError;
import static java.lang.invoke.MethodType.methodType;
/**
@ -117,14 +120,15 @@ public class MethodHandles {
}
/**
* This reflected$lookup method is the alternate implementation of
* the lookup method when being invoked by reflection.
* This lookup method is the alternate implementation of
* the lookup method with a leading caller class argument which is
* non-caller-sensitive. This method is only invoked by reflection
* and method handle.
*/
@CallerSensitive
private static Lookup reflected$lookup() {
Class<?> caller = Reflection.getCallerClass();
@CallerSensitiveAdapter
private static Lookup lookup(Class<?> caller) {
if (caller.getClassLoader() == null) {
throw newIllegalArgumentException("illegal lookupClass: "+caller);
throw newInternalError("calling lookup() reflectively is not supported: "+caller);
}
return new Lookup(caller);
}
@ -329,7 +333,7 @@ public class MethodHandles {
throw new IllegalAccessException(caller + " does not have ORIGINAL access");
}
Object classdata = MethodHandleNatives.classData(caller.lookupClass());
Object classdata = classData(caller.lookupClass());
if (classdata == null) return null;
try {
@ -341,6 +345,17 @@ public class MethodHandles {
}
}
/*
* Returns the class data set by the VM in the Class::classData field.
*
* This is also invoked by LambdaForms as it cannot use condy via
* MethodHandles::classData due to bootstrapping issue.
*/
static Object classData(Class<?> c) {
UNSAFE.ensureClassInitialized(c);
return SharedSecrets.getJavaLangAccess().classData(c);
}
/**
* Returns the element at the specified index in the
* {@linkplain #classData(Lookup, String, Class) class data},
@ -2359,15 +2374,16 @@ public class MethodHandles {
/**
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
* from the given bytes. No package name check on the given name.
* from the given bytes and the given options. No package name check on the given name.
*
* @param name fully-qualified name that specifies the prefix of the hidden class
* @param bytes class bytes
* @return ClassDefiner that defines a hidden class of the given bytes.
* @param options class options
* @return ClassDefiner that defines a hidden class of the given bytes and options.
*/
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes) {
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, Set<ClassOption> options) {
// skip name and access flags validation
return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), Set.of(), false);
return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), options, false);
}
/**

View File

@ -26,6 +26,7 @@
package java.lang.reflect;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.ConstructorAccessor;
import jdk.internal.reflect.Reflection;
@ -63,19 +64,17 @@ import java.util.StringJoiner;
* @since 1.1
*/
public final class Constructor<T> extends Executable {
@Stable
private Class<T> clazz;
private int slot;
private Class<?>[] parameterTypes;
private Class<?>[] exceptionTypes;
@Stable
private int modifiers;
private final Class<T> clazz;
private final int slot;
private final Class<?>[] parameterTypes;
private final Class<?>[] exceptionTypes;
private final int modifiers;
// Generics and annotations support
private transient String signature;
private final transient String signature;
// generic info repository; lazily initialized
private transient ConstructorRepository genericInfo;
private byte[] annotations;
private byte[] parameterAnnotations;
private final byte[] annotations;
private final byte[] parameterAnnotations;
// Generics infrastructure
// Accessor for factory
@ -492,9 +491,6 @@ public final class Constructor<T> extends Executable {
if (checkAccess)
checkAccess(caller, clazz, clazz, modifiers);
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read @Stable
if (ca == null) {
ca = acquireConstructorAccessor();
@ -534,6 +530,7 @@ public final class Constructor<T> extends Executable {
// synchronization will probably make the implementation more
// scalable.
private ConstructorAccessor acquireConstructorAccessor() {
// First check to see if one has been created yet, and take it
// if so.
Constructor<?> root = this.root;
@ -542,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);
}

View File

@ -65,23 +65,19 @@ import sun.reflect.annotation.TypeAnnotationParser;
*/
public final
class Field extends AccessibleObject implements Member {
@Stable
private Class<?> clazz;
private int slot;
private final Class<?> clazz;
private final int slot;
// This is guaranteed to be interned by the VM in the 1.4
// reflection implementation
private String name;
@Stable
private Class<?> type;
@Stable
private int modifiers;
private boolean trustedFinal;
private final String name;
private final Class<?> type;
private final int modifiers;
private final boolean trustedFinal;
// Generics and annotations support
private transient String signature;
private final transient String signature;
// generic info repository; lazily initialized
private transient FieldRepository genericInfo;
private byte[] annotations;
private final byte[] annotations;
// Cached field accessor created without override
@Stable
private FieldAccessor fieldAccessor;

View File

@ -26,7 +26,9 @@
package java.lang.reflect;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.CallerSensitiveAdapter;
import jdk.internal.reflect.MethodAccessor;
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.ForceInline;
@ -67,24 +69,22 @@ import java.util.StringJoiner;
* @since 1.1
*/
public final class Method extends Executable {
@Stable
private Class<?> clazz;
private int slot;
private final Class<?> clazz;
private final int slot;
// This is guaranteed to be interned by the VM in the 1.4
// reflection implementation
private String name;
private Class<?> returnType;
private Class<?>[] parameterTypes;
private Class<?>[] exceptionTypes;
@Stable
private int modifiers;
private final String name;
private final Class<?> returnType;
private final Class<?>[] parameterTypes;
private final Class<?>[] exceptionTypes;
private final int modifiers;
// Generics and annotations support
private transient String signature;
private final transient String signature;
// generic info repository; lazily initialized
private transient MethodRepository genericInfo;
private byte[] annotations;
private byte[] parameterAnnotations;
private byte[] annotationDefault;
private final byte[] annotations;
private final byte[] parameterAnnotations;
private final byte[] annotationDefault;
@Stable
private MethodAccessor methodAccessor;
// For sharing of MethodAccessors. This branching structure is
@ -553,20 +553,64 @@ public final class Method extends Executable {
@ForceInline // to ensure Reflection.getCallerClass optimization
@IntrinsicCandidate
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
throws IllegalAccessException, InvocationTargetException
{
boolean callerSensitive = isCallerSensitive();
Class<?> caller = null;
if (!override || callerSensitive) {
caller = Reflection.getCallerClass();
}
// Reflection::getCallerClass filters all subclasses of
// jdk.internal.reflect.MethodAccessorImpl and Method::invoke(Object, Object[])
// Should not call Method::invoke(Object, Object[], Class) here
if (!override) {
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);
}

View File

@ -28,8 +28,11 @@ package jdk.internal.access;
import jdk.internal.invoke.NativeEntryPoint;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.ByteOrder;
import java.util.List;
import java.util.Map;
@ -139,4 +142,43 @@ public interface JavaLangInvokeAccess {
* @param mh the method handle
*/
void ensureCustomized(MethodHandle mh);
/**
* Produces a method handle unreflecting from a {@code Constructor} with
* the trusted lookup
*/
MethodHandle unreflectConstructor(Constructor<?> ctor) throws IllegalAccessException;
/**
* Produces a method handle unreflecting from a {@code Field} with
* the trusted lookup
*/
MethodHandle unreflectField(Field field, boolean isSetter) throws IllegalAccessException;
/**
* Produces a method handle of a virtual method with the trusted lookup.
*/
MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws IllegalAccessException;
/**
* Produces a method handle of a static method with the trusted lookup.
*/
MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws IllegalAccessException;
/**
* Returns a method handle of an invoker class injected for core reflection
* implementation with the following signature:
* reflect_invoke_V(MethodHandle mh, Object target, Object[] args)
*
* The invoker class is a hidden class which has the same
* defining class loader, runtime package, and protection domain
* as the given caller class.
*/
MethodHandle reflectiveInvoker(Class<?> caller);
/**
* Defines a hidden class of the given name and bytes with class data.
* The given bytes is trusted.
*/
Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize);
}

View File

@ -51,7 +51,6 @@ import java.security.Signature;
for this purpose, namely the loss of compile-time checking. */
public class SharedSecrets {
private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
private static JavaAWTAccess javaAWTAccess;
private static JavaAWTFontAccess javaAWTFontAccess;
private static JavaBeansAccess javaBeansAccess;

View File

@ -27,14 +27,13 @@ package jdk.internal.misc;
import static java.lang.Thread.State.*;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.annotation.Stable;
import sun.nio.ch.FileChannelImpl;
public class VM {
@ -91,7 +90,19 @@ public class VM {
* @see java.lang.System#initPhase2
*/
public static boolean isModuleSystemInited() {
return VM.initLevel() >= MODULE_SYSTEM_INITED;
return initLevel >= MODULE_SYSTEM_INITED;
}
private static @Stable boolean javaLangInvokeInited;
public static void setJavaLangInvokeInited() {
if (javaLangInvokeInited) {
throw new InternalError("java.lang.invoke already inited");
}
javaLangInvokeInited = true;
}
public static boolean isJavaLangInvokeInited() {
return javaLangInvokeInited;
}
/**

View File

@ -0,0 +1,109 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.WrongMethodTypeException;
import java.util.Set;
/**
* Utility methods used by DirectMethodHandleAccessor and DirectConstructorHandleAccessor
*/
public class AccessorUtils {
/**
* Determines if the given exception thrown by MethodHandle::invokeExact
* is caused by an illegal argument passed to Method::invoke or
* Constructor::newInstance. This method inspects the stack trace of
* the exception to detect if it is thrown by the method handle core
* implementation or the implementation of the reflected method or constructor.
*
* MethodHandle::invoke throws ClassCastException if the receiver object
* is not an instance of the declaring class of the method if the method
* is an instance method, or if a parameter value cannot be converted
* to the corresponding formal parameter type. It throws
* NullPointerException if the receiver object is null if the method
* is an instance method, or if unboxing operation of a parameter fails
* because the parameter value is null. It throws WrongMethodTypeException
* if the method type mismatches.
*
* @param accessorType the accessor class that does the method handle invocation
* @param e ClassCastException, NullPointerException or WrongMethodTypeException
*/
static boolean isIllegalArgument(Class<?> accessorType, RuntimeException e) {
assert(e instanceof ClassCastException || e instanceof NullPointerException ||
e instanceof WrongMethodTypeException);
StackTraceElement[] stackTrace = e.getStackTrace();
if (stackTrace.length == 0) {
return false; // would this happen?
}
int i = 0;
StackTraceElement frame = stackTrace[0];
// Class::cast and Objects::requiresNonNull may be thrown by the implementation
// of the reflected method/constructor. Skip them and continue.
if ((frame.getClassName().equals("java.lang.Class") && frame.getMethodName().equals("cast"))
|| (frame.getClassName().equals("java.util.Objects") && frame.getMethodName().equals("requiresNonNull"))) {
i++;
}
for (; i < stackTrace.length; i++) {
frame = stackTrace[i];
String cname = frame.getClassName();
// it's illegal argument if this exception is thrown from accessorType
if (cname.equals(accessorType.getName())) {
return true;
}
// if this exception is thrown from an unnamed module or not from java.base
// then i.e. not from method handle core implementation
if (frame.getModuleName() == null || !frame.getModuleName().equals("java.base")) {
return false;
}
int index = cname.lastIndexOf(".");
String pn = index > 0 ? cname.substring(0, index) : "";
// exception thrown from java.base but not from core reflection/method handle internals
if (!IMPL_PACKAGES.contains(pn)) {
return false;
}
// If Constructor::newInstance is invoked by Method::invoke or vice versa,
// so the exception is thrown from the implementation body of the reflected
// method or constructor
if ((accessorType == DirectMethodHandleAccessor.class
&& cname.startsWith(DirectConstructorHandleAccessor.class.getName()))
|| (accessorType == DirectConstructorHandleAccessor.class
&& cname.startsWith(DirectMethodHandleAccessor.class.getName()))) {
// thrown from another reflection accessor impl class
return false;
}
}
return false;
}
private static final Set<String> IMPL_PACKAGES = Set.of(
"java.lang.reflect",
"java.lang.invoke",
"jdk.internal.reflect",
"sun.invoke.util"
);
}

View File

@ -0,0 +1,112 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
/**
* A method annotated @CallerSensitiveAdapter is an adapter method corresponding
* to a caller-sensitive method, which is annotated with @CallerSensitive.
*
* A caller-sensitive adapter is private and has the same name as its
* corresponding caller-sensitive method with a trailing caller class parameter.
*
* When a caller-sensitive method is invoked via Method::invoke or MethodHandle
* the core reflection and method handle implementation will find if
* an @CallerSensitiveAdapter method for that CSM is present. If present,
* the runtime will invoke the adapter method with the caller class
* argument instead. This special calling sequence ensures that the same caller
* class is passed to a caller-sensitive method via Method::invoke,
* MethodHandle::invokeExact, or a mix of these methods.
*
* For example, CSM::returnCallerClass is a caller-sensitive method
* with an adapter method:
* {@code
* class CSM {
* @CallerSensitive
* static Class<?> returnCallerClass() {
* return returnCallerClass(Reflection.getCallerClass());
* }
* @CallerSensitiveAdapter
* private static Class<?> returnCallerClass(Class<?> caller) {
* return caller;
* }
* }
*
* class Test {
* void test() throws Throwable {
* // calling CSM::returnCallerClass via reflection
* var csm = CSM.class.getMethod("returnCallerClass");
* // expect Foo to be the caller class
* var caller = csm.invoke(null);
* assert(caller == Test.class);
* }
* void test2() throws Throwable {
* // method handle for Method::invoke
* MethodHandle mh = MethodHandles.lookup().findVirtual(Method.class, "invoke",
* methodType(Object.class, Object.class, Object[].class));
* var csm = CSM.class.getMethod("returnCallerClass");
* // invoke Method::invoke via method handle and the target method
* // being invoked reflectively is CSM::returnCallerClass
* var caller = mh.invoke(csm, null, null);
* assert(caller == Test.class);
* }
* }
* }
*
*
* Both CSM::returnCallerClass and Method::invoke can have an adapter method
* with a caller-class parameter defined. Test::test calls CSM::returnCallerClass
* via Method::invoke which does the stack walking to find the caller's class.
* It will pass the caller's class directly to the adapter method for
* CSM::returnCallerClass.
*
* Similarly, Test::test2 invokes the method handle of Method::invoke to
* call CSM::returnCallerClass reflectively. In that case, MethodHandle::invokeExact
* uses the lookup class of the Lookup object producing the method handle
* as the caller's class, so no stack walking involved. The lookup class is Test.
* It will invoke the adapter method of Method::invoke with Test as the caller's
* class, which in turn invokes the adapter method of CSM::returnCallerClass
* with Test as the caller. The calling sequence eliminates the need for
* multiple stack walks when a caller-sensitive method is invoked reflectively.
*
* For caller-sensitive methods that require an exact caller class, the adapter
* method must be defined for correctness. {@link java.lang.invoke.MethodHandles#lookup()}
* and {@link ClassLoader#registerAsParallelCapable()} are the only two methods
* in the JDK that need the exact caller class.
*
* On the other hand, for caller-sensitive methods that use the caller's class
* for access checks or security permission checks, i.e., based on its runtime
* package, defining loader, or protection domain, the adapter method is optional.
*/
@Retention(RetentionPolicy.CLASS)
@Target({METHOD})
public @interface CallerSensitiveAdapter {
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* MethodAccessor adapter for caller-sensitive methods which have
* an alternate non-CSM method with the same method name but an additional
* caller class argument.
*
* When a caller-sensitive method is called,
* Method::invoke(Object target, Object[] args, Class<?> caller) will
* be invoked with the caller class. If an adapter is present,
* the adapter method with the caller class parameter will be called
* instead.
*/
class CsMethodAccessorAdapter extends MethodAccessorImpl {
private final Method csmAdapter;
private final MethodAccessor accessor;
CsMethodAccessorAdapter(Method method, Method csmAdapter, MethodAccessor accessor) {
assert Reflection.isCallerSensitive(method) && !Reflection.isCallerSensitive(csmAdapter);
this.csmAdapter = csmAdapter;
this.accessor = accessor;
}
@Override
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException {
throw new InternalError("caller-sensitive method invoked without explicit caller: " + csmAdapter);
}
@Override
@ForceInline
@Hidden
public Object invoke(Object obj, Object[] args, Class<?> caller)
throws IllegalArgumentException, InvocationTargetException {
Object[] newArgs = new Object[args == null ? 1 : args.length + 1];
newArgs[0] = caller;
if (args != null) {
System.arraycopy(args, 0, newArgs, 1, args.length);
}
return accessor.invoke(obj, newArgs);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -35,7 +35,6 @@ import java.util.Objects;
class DelegatingConstructorAccessorImpl extends ConstructorAccessorImpl {
// initial non-null delegate
@Stable
private final ConstructorAccessorImpl initialDelegate;
// alternative delegate: starts as null;
// only single change from null -> non-null is guaranteed

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -35,10 +35,11 @@ import java.util.Objects;
class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
// initial non-null delegate
@Stable private final MethodAccessorImpl initialDelegate;
private final MethodAccessorImpl initialDelegate;
// alternative delegate: starts as null;
// only single change from null -> non-null is guaranteed
@Stable private MethodAccessorImpl altDelegate;
@Stable
private MethodAccessorImpl altDelegate;
DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
initialDelegate = Objects.requireNonNull(delegate);
@ -51,6 +52,13 @@ class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
return delegate().invoke(obj, args);
}
@Override
public Object invoke(Object obj, Object[] args, Class<?> caller)
throws IllegalArgumentException, InvocationTargetException
{
return delegate().invoke(obj, args, caller);
}
private MethodAccessorImpl delegate() {
var d = altDelegate;
return d != null ? d : initialDelegate;

View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.WrongMethodTypeException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import static jdk.internal.reflect.MethodHandleAccessorFactory.SPECIALIZED_PARAM_COUNT;
class DirectConstructorHandleAccessor extends ConstructorAccessorImpl {
static ConstructorAccessorImpl constructorAccessor(Constructor<?> ctor, MethodHandle target) {
return new DirectConstructorHandleAccessor(ctor, target);
}
static ConstructorAccessorImpl nativeAccessor(Constructor<?> ctor) {
return new NativeAccessor(ctor);
}
private static final int PARAM_COUNT_MASK = 0x00FF;
private static final int NONZERO_BIT = 0x8000_0000;
private final int paramFlags;
private final MethodHandle target;
DirectConstructorHandleAccessor(Constructor<?> ctor, MethodHandle target) {
this.paramFlags = (ctor.getParameterCount() & PARAM_COUNT_MASK) | NONZERO_BIT;
this.target = target;
}
@Override
public Object newInstance(Object[] args) throws InstantiationException, InvocationTargetException {
int argc = args != null ? args.length : 0;
// only check argument count for specialized forms
int paramCount = paramFlags & PARAM_COUNT_MASK;
if (paramCount <= SPECIALIZED_PARAM_COUNT && argc != paramCount) {
throw new IllegalArgumentException("wrong number of arguments: " + argc + " expected: " + paramCount);
}
try {
return invokeImpl(args);
} catch (ClassCastException|WrongMethodTypeException e) {
if (isIllegalArgument(e))
throw new IllegalArgumentException("argument type mismatch", e);
else
throw new InvocationTargetException(e);
} catch (NullPointerException e) {
if (isIllegalArgument(e))
throw new IllegalArgumentException(e);
else
throw new InvocationTargetException(e);
} catch (Throwable e) {
throw new InvocationTargetException(e);
}
}
private boolean isIllegalArgument(RuntimeException ex) {
return AccessorUtils.isIllegalArgument(DirectConstructorHandleAccessor.class, ex);
}
@Hidden
@ForceInline
Object invokeImpl(Object[] args) throws Throwable {
return switch (paramFlags & PARAM_COUNT_MASK) {
case 0 -> target.invokeExact();
case 1 -> target.invokeExact(args[0]);
case 2 -> target.invokeExact(args[0], args[1]);
case 3 -> target.invokeExact(args[0], args[1], args[2]);
default -> target.invokeExact(args);
};
}
/**
* Invoke the constructor via native VM reflection
*/
static class NativeAccessor extends ConstructorAccessorImpl {
private final Constructor<?> ctor;
NativeAccessor(Constructor<?> ctor) {
this.ctor = ctor;
}
@Override
public Object newInstance(Object[] args) throws InstantiationException, InvocationTargetException {
return newInstance0(ctor, args);
}
private static native Object newInstance0(Constructor<?> c, Object[] args)
throws InstantiationException, InvocationTargetException;
}
}

View File

@ -0,0 +1,361 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.WrongMethodTypeException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import static java.lang.invoke.MethodType.genericMethodType;
import static jdk.internal.reflect.MethodHandleAccessorFactory.SPECIALIZED_PARAM_COUNT;
import static jdk.internal.reflect.MethodHandleAccessorFactory.LazyStaticHolder.JLIA;
class DirectMethodHandleAccessor extends MethodAccessorImpl {
/**
* Creates a MethodAccessorImpl for a non-native method.
*/
static MethodAccessorImpl methodAccessor(Method method, MethodHandle target) {
assert !Modifier.isNative(method.getModifiers());
return new DirectMethodHandleAccessor(method, target, false);
}
/**
* Creates MethodAccessorImpl for the adapter method for a caller-sensitive method.
* The given target method handle is the adapter method with the additional caller class
* parameter.
*/
static MethodAccessorImpl callerSensitiveAdapter(Method original, MethodHandle target) {
assert Reflection.isCallerSensitive(original);
// for CSM adapter method with the additional caller class parameter
// creates the adaptive method accessor only.
return new DirectMethodHandleAccessor(original, target, true);
}
/**
* Creates MethodAccessorImpl that invokes the given method via VM native reflection
* support. This is used for native methods. It can be used for java methods
* during early VM startup.
*/
static MethodAccessorImpl nativeAccessor(Method method, boolean callerSensitive) {
return callerSensitive ? new NativeAccessor(method, findCSMethodAdapter(method))
: new NativeAccessor(method);
}
private static final int PARAM_COUNT_MASK = 0x00FF;
private static final int HAS_CALLER_PARAM_BIT = 0x0100;
private static final int IS_STATIC_BIT = 0x0200;
private static final int NONZERO_BIT = 0x8000_0000;
private final Class<?> declaringClass;
private final int paramCount;
private final int flags;
private final MethodHandle target;
DirectMethodHandleAccessor(Method method, MethodHandle target, boolean hasCallerParameter) {
this.declaringClass = method.getDeclaringClass();
this.paramCount = method.getParameterCount();
this.flags = (hasCallerParameter ? HAS_CALLER_PARAM_BIT : 0) |
(Modifier.isStatic(method.getModifiers()) ? IS_STATIC_BIT : 0);
this.target = target;
}
@Override
@ForceInline
public Object invoke(Object obj, Object[] args) throws InvocationTargetException {
if (!isStatic()) {
checkReceiver(obj);
}
checkArgumentCount(paramCount, args);
try {
return invokeImpl(obj, args);
} catch (ClassCastException | WrongMethodTypeException e) {
if (isIllegalArgument(e)) {
// No cause in IAE to be consistent with the old behavior
throw new IllegalArgumentException("argument type mismatch");
} else {
throw new InvocationTargetException(e);
}
} catch (NullPointerException e) {
if (isIllegalArgument(e)) {
throw new IllegalArgumentException(e);
} else {
throw new InvocationTargetException(e);
}
} catch (Throwable e) {
throw new InvocationTargetException(e);
}
}
@Override
@ForceInline
public Object invoke(Object obj, Object[] args, Class<?> caller) throws InvocationTargetException {
if (!isStatic()) {
checkReceiver(obj);
}
checkArgumentCount(paramCount, args);
try {
return invokeImpl(obj, args, caller);
} catch (ClassCastException | WrongMethodTypeException e) {
if (isIllegalArgument(e)) {
// No cause in IAE to be consistent with the old behavior
throw new IllegalArgumentException("argument type mismatch");
} else {
throw new InvocationTargetException(e);
}
} catch (NullPointerException e) {
if (isIllegalArgument(e)) {
throw new IllegalArgumentException(e);
} else {
throw new InvocationTargetException(e);
}
} catch (Throwable e) {
throw new InvocationTargetException(e);
}
}
@Hidden
@ForceInline
private Object invokeImpl(Object obj, Object[] args) throws Throwable {
return switch (paramCount) {
case 0 -> target.invokeExact(obj);
case 1 -> target.invokeExact(obj, args[0]);
case 2 -> target.invokeExact(obj, args[0], args[1]);
case 3 -> target.invokeExact(obj, args[0], args[1], args[2]);
default -> target.invokeExact(obj, args);
};
}
@Hidden
@ForceInline
private Object invokeImpl(Object obj, Object[] args, Class<?> caller) throws Throwable {
if (hasCallerParameter()) {
// caller-sensitive method is invoked through method with caller parameter
return switch (paramCount) {
case 0 -> target.invokeExact(obj, caller);
case 1 -> target.invokeExact(obj, args[0], caller);
case 2 -> target.invokeExact(obj, args[0], args[1], caller);
case 3 -> target.invokeExact(obj, args[0], args[1], args[2], caller);
default -> target.invokeExact(obj, args, caller);
};
} else {
// caller-sensitive method is invoked through a per-caller invoker while
// the target MH is always spreading the args
var invoker = JLIA.reflectiveInvoker(caller);
try {
// invoke the target method handle via an invoker
return invoker.invokeExact(target, obj, args);
} catch (IllegalArgumentException e) {
throw new InvocationTargetException(e);
}
}
}
private boolean isStatic() {
return (flags & IS_STATIC_BIT) == IS_STATIC_BIT;
}
private boolean hasCallerParameter() {
return (flags & HAS_CALLER_PARAM_BIT) == HAS_CALLER_PARAM_BIT;
}
private boolean isIllegalArgument(RuntimeException ex) {
return AccessorUtils.isIllegalArgument(DirectMethodHandleAccessor.class, ex);
}
private void checkReceiver(Object o) {
// NOTE: will throw NullPointerException, as specified, if o is null
if (!declaringClass.isAssignableFrom(o.getClass())) {
throw new IllegalArgumentException("object is not an instance of declaring class");
}
}
/**
* Invoke the method via native VM reflection
*/
static class NativeAccessor extends MethodAccessorImpl {
private final Method method;
private final Method csmAdapter;
private final boolean callerSensitive;
NativeAccessor(Method method) {
assert !Reflection.isCallerSensitive(method);
this.method = method;
this.csmAdapter = null;
this.callerSensitive = false;
}
NativeAccessor(Method method, Method csmAdapter) {
assert Reflection.isCallerSensitive(method);
this.method = method;
this.csmAdapter = csmAdapter;
this.callerSensitive = true;
}
@Override
public Object invoke(Object obj, Object[] args) throws InvocationTargetException {
assert csmAdapter == null;
return invoke0(method, obj, args);
}
@Override
public Object invoke(Object obj, Object[] args, Class<?> caller) throws InvocationTargetException {
assert callerSensitive;
if (csmAdapter != null) {
Object[] newArgs = new Object[csmAdapter.getParameterCount()];
newArgs[0] = caller;
if (args != null) {
System.arraycopy(args, 0, newArgs, 1, args.length);
}
return invoke0(csmAdapter, obj, newArgs);
} else {
assert VM.isJavaLangInvokeInited();
try {
return ReflectiveInvoker.invoke(methodAccessorInvoker(), caller, obj, args);
} catch (InvocationTargetException|RuntimeException|Error e) {
throw e;
} catch (Throwable e) {
throw new InternalError(e);
}
}
}
public Object invokeViaReflectiveInvoker(Object obj, Object[] args) throws InvocationTargetException {
return invoke0(method, obj, args);
}
/*
* A method handle to invoke Reflective::Invoker
*/
private MethodHandle maInvoker;
private MethodHandle methodAccessorInvoker() {
MethodHandle invoker = maInvoker;
if (invoker == null) {
maInvoker = invoker = ReflectiveInvoker.bindTo(this);
}
return invoker;
}
private static native Object invoke0(Method m, Object obj, Object[] args);
static class ReflectiveInvoker {
/**
* Return a method handle for NativeAccessor::invoke bound to the given accessor object
*/
static MethodHandle bindTo(NativeAccessor accessor) {
return NATIVE_ACCESSOR_INVOKE.bindTo(accessor);
}
/*
* When Method::invoke on a caller-sensitive method is to be invoked
* and no adapter method with an additional caller class argument is defined,
* the caller-sensitive method must be invoked via an invoker injected
* which has the following signature:
* reflect_invoke_V(MethodHandle mh, Object target, Object[] args)
*
* The stack frames calling the method `csm` through reflection will
* look like this:
* obj.csm(args)
* NativeAccessor::invoke(obj, args)
* InjectedInvoker::reflect_invoke_V(vamh, obj, args);
* method::invoke(obj, args)
* p.Foo::m
*
* An injected invoker class is a hidden class which has the same
* defining class loader, runtime package, and protection domain
* as the given caller class.
*
* The caller-sensitive method will call Reflection::getCallerClass
* to get the caller class.
*/
static Object invoke(MethodHandle target, Class<?> caller, Object obj, Object[] args)
throws InvocationTargetException
{
var reflectInvoker = JLIA.reflectiveInvoker(caller);
try {
return reflectInvoker.invokeExact(target, obj, args);
} catch (InvocationTargetException | RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new InternalError(e);
}
}
static final JavaLangInvokeAccess JLIA;
static final MethodHandle NATIVE_ACCESSOR_INVOKE;
static {
try {
JLIA = SharedSecrets.getJavaLangInvokeAccess();
NATIVE_ACCESSOR_INVOKE = MethodHandles.lookup().findVirtual(NativeAccessor.class, "invoke",
genericMethodType(1, true));
} catch (NoSuchMethodException|IllegalAccessException e) {
throw new InternalError(e);
}
}
}
}
private static void checkArgumentCount(int paramCount, Object[] args) {
// only check argument count for specialized forms
if (paramCount > SPECIALIZED_PARAM_COUNT) return;
int argc = args != null ? args.length : 0;
if (argc != paramCount) {
throw new IllegalArgumentException("wrong number of arguments: " + argc + " expected: " + paramCount);
}
}
/**
* Returns an adapter for caller-sensitive method if present.
* Otherwise, null.
*
* A trusted method can define an adapter method for a caller-sensitive method `foo`
* with an additional caller class argument that will be invoked reflectively.
*/
private static Method findCSMethodAdapter(Method method) {
if (!Reflection.isCallerSensitive(method)) return null;
int paramCount = method.getParameterCount();
Class<?>[] ptypes = new Class<?>[paramCount+1];
ptypes[paramCount] = Class.class;
System.arraycopy(method.getParameterTypes(), 0, ptypes, 0, paramCount);
try {
return method.getDeclaringClass().getDeclaredMethod(method.getName(), ptypes);
} catch (NoSuchMethodException ex) {
return null;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,9 +25,8 @@
package jdk.internal.reflect;
import jdk.internal.vm.annotation.Stable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/** Package-private implementation of the FieldAccessor interface
which has access to all classes and all fields, regardless of
@ -35,7 +34,6 @@ import java.lang.reflect.Field;
abstract class FieldAccessorImpl extends MagicAccessorImpl
implements FieldAccessor {
@Stable
protected final Field field;
FieldAccessorImpl(Field field) {
@ -113,4 +111,167 @@ abstract class FieldAccessorImpl extends MagicAccessorImpl
/** Matches specification in {@link java.lang.reflect.Field} */
public abstract void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException;
protected void ensureObj(Object o) {
// NOTE: will throw NullPointerException, as specified, if o is null
if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
throwSetIllegalArgumentException(o);
}
}
private String getQualifiedFieldName() {
return field.getDeclaringClass().getName() + "." +field.getName();
}
protected IllegalArgumentException newGetIllegalArgumentException(String type) {
return new IllegalArgumentException(
"Attempt to get "+field.getType().getName()+" field \"" +
getQualifiedFieldName() + "\" with illegal data type conversion to "+type
);
}
protected void throwFinalFieldIllegalAccessException(String attemptedType,
String attemptedValue)
throws IllegalAccessException {
throw new IllegalAccessException(getSetMessage(attemptedType, attemptedValue));
}
protected void throwFinalFieldIllegalAccessException(Object o) throws IllegalAccessException {
throwFinalFieldIllegalAccessException(o != null ? o.getClass().getName() : "", "");
}
protected void throwFinalFieldIllegalAccessException(boolean z) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("boolean", Boolean.toString(z));
}
protected void throwFinalFieldIllegalAccessException(char b) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("char", Character.toString(b));
}
protected void throwFinalFieldIllegalAccessException(byte b) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("byte", Byte.toString(b));
}
protected void throwFinalFieldIllegalAccessException(short b) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("short", Short.toString(b));
}
protected void throwFinalFieldIllegalAccessException(int i) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("int", Integer.toString(i));
}
protected void throwFinalFieldIllegalAccessException(long i) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("long", Long.toString(i));
}
protected void throwFinalFieldIllegalAccessException(float f) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("float", Float.toString(f));
}
protected void throwFinalFieldIllegalAccessException(double f) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("double", Double.toString(f));
}
protected IllegalArgumentException newGetBooleanIllegalArgumentException() {
return newGetIllegalArgumentException("boolean");
}
protected IllegalArgumentException newGetByteIllegalArgumentException() {
return newGetIllegalArgumentException("byte");
}
protected IllegalArgumentException newGetCharIllegalArgumentException() {
return newGetIllegalArgumentException("char");
}
protected IllegalArgumentException newGetShortIllegalArgumentException() {
return newGetIllegalArgumentException("short");
}
protected IllegalArgumentException newGetIntIllegalArgumentException() {
return newGetIllegalArgumentException("int");
}
protected IllegalArgumentException newGetLongIllegalArgumentException() {
return newGetIllegalArgumentException("long");
}
protected IllegalArgumentException newGetFloatIllegalArgumentException() {
return newGetIllegalArgumentException("float");
}
protected IllegalArgumentException newGetDoubleIllegalArgumentException() {
return newGetIllegalArgumentException("double");
}
protected String getSetMessage(String attemptedType, String attemptedValue) {
String err = "Can not set";
if (Modifier.isStatic(field.getModifiers()))
err += " static";
if (Modifier.isFinal(field.getModifiers()))
err += " final";
err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " to ";
if (!attemptedValue.isEmpty()) {
err += "(" + attemptedType + ")" + attemptedValue;
} else {
if (!attemptedType.isEmpty())
err += attemptedType;
else
err += "null value";
}
return err;
}
protected String getMessage(boolean getter, String attemptedType) {
String err = "Can not " + (getter ? "get" : "set");
if (Modifier.isStatic(field.getModifiers()))
err += " static";
if (Modifier.isFinal(field.getModifiers()))
err += " final";
err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " on " + attemptedType;
return err;
}
protected void throwSetIllegalArgumentException(String attemptedType,
String attemptedValue) {
throw new IllegalArgumentException(getSetMessage(attemptedType,attemptedValue));
}
protected void throwSetIllegalArgumentException(Object o) {
throwSetIllegalArgumentException(o != null ? o.getClass().getName() : "", "");
}
protected void throwSetIllegalArgumentException(boolean b) {
throwSetIllegalArgumentException("boolean", Boolean.toString(b));
}
protected void throwSetIllegalArgumentException(byte b) {
throwSetIllegalArgumentException("byte", Byte.toString(b));
}
protected void throwSetIllegalArgumentException(char c) {
throwSetIllegalArgumentException("char", Character.toString(c));
}
protected void throwSetIllegalArgumentException(short s) {
throwSetIllegalArgumentException("short", Short.toString(s));
}
protected void throwSetIllegalArgumentException(int i) {
throwSetIllegalArgumentException("int", Integer.toString(i));
}
protected void throwSetIllegalArgumentException(long l) {
throwSetIllegalArgumentException("long", Long.toString(l));
}
protected void throwSetIllegalArgumentException(float f) {
throwSetIllegalArgumentException("float", Float.toString(f));
}
protected void throwSetIllegalArgumentException(double d) {
throwSetIllegalArgumentException("double", Double.toString(d));
}
}

View File

@ -37,4 +37,7 @@ public interface MethodAccessor {
/** Matches specification in {@link java.lang.reflect.Method} */
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException;
public Object invoke(Object obj, Object[] args, Class<?> caller)
throws IllegalArgumentException, InvocationTargetException;
}

View File

@ -28,8 +28,8 @@ package jdk.internal.reflect;
import java.security.AccessController;
import java.security.PrivilegedAction;
/** Generator for sun.reflect.MethodAccessor and
sun.reflect.ConstructorAccessor objects using bytecodes to
/** Generator for jdk.internal.reflect.MethodAccessor and
jdk.internal.reflect.ConstructorAccessor objects using bytecodes to
implement reflection. A java.lang.reflect.Method or
java.lang.reflect.Constructor object can delegate its invoke or
newInstance method to an accessor using native code or to one

View File

@ -45,4 +45,9 @@ abstract class MethodAccessorImpl extends MagicAccessorImpl
/** Matches specification in {@link java.lang.reflect.Method} */
public abstract Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException;
public Object invoke(Object obj, Object[] args, Class<?> caller)
throws IllegalArgumentException, InvocationTargetException {
return invoke(obj, args);
}
}

View File

@ -0,0 +1,366 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
import static java.lang.invoke.MethodType.genericMethodType;
import static java.lang.invoke.MethodType.methodType;
import static jdk.internal.reflect.MethodHandleAccessorFactory.LazyStaticHolder.*;
final class MethodHandleAccessorFactory {
/**
* Creates a MethodAccessor for the given reflected method.
*
* If the given method is called before the java.lang.invoke initialization
* or the given method is a native method, it will use the native VM reflection
* support.
*
* If the given method is a caller-sensitive method and the corresponding
* caller-sensitive adapter with the caller class parameter is present,
* it will use the method handle of the caller-sensitive adapter.
*
* Otherwise, it will use the direct method handle of the given method.
*
* @see CallerSensitive
* @see CallerSensitiveAdapter
*/
static MethodAccessorImpl newMethodAccessor(Method method, boolean callerSensitive) {
if (useNativeAccessor(method)) {
return DirectMethodHandleAccessor.nativeAccessor(method, callerSensitive);
}
// ExceptionInInitializerError may be thrown during class initialization
// Ensure class initialized outside the invocation of method handle
// so that EIIE is propagated (not wrapped with ITE)
ensureClassInitialized(method.getDeclaringClass());
try {
if (callerSensitive) {
var dmh = findCallerSensitiveAdapter(method);
if (dmh != null) {
return DirectMethodHandleAccessor.callerSensitiveAdapter(method, dmh);
}
}
var dmh = getDirectMethod(method, callerSensitive);
return DirectMethodHandleAccessor.methodAccessor(method, dmh);
} catch (IllegalAccessException e) {
throw new InternalError(e);
}
}
/**
* Creates a ConstructorAccessor for the given reflected constructor.
*
* If a given constructor is called before the java.lang.invoke initialization,
* it will use the native VM reflection support.
*
* Otherwise, it will use the direct method handle of the given constructor.
*/
static ConstructorAccessorImpl newConstructorAccessor(Constructor<?> ctor) {
if (useNativeAccessor(ctor)) {
return DirectConstructorHandleAccessor.nativeAccessor(ctor);
}
// ExceptionInInitializerError may be thrown during class initialization
// Ensure class initialized outside the invocation of method handle
// so that EIIE is propagated (not wrapped with ITE)
ensureClassInitialized(ctor.getDeclaringClass());
try {
MethodHandle mh = JLIA.unreflectConstructor(ctor);
int paramCount = mh.type().parameterCount();
MethodHandle target = mh.asFixedArity();
MethodType mtype = specializedMethodTypeForConstructor(paramCount);
if (paramCount > SPECIALIZED_PARAM_COUNT) {
// spread the parameters only for the non-specialized case
target = target.asSpreader(Object[].class, paramCount);
}
target = target.asType(mtype);
return DirectConstructorHandleAccessor.constructorAccessor(ctor, target);
} catch (IllegalAccessException e) {
throw new InternalError(e);
}
}
/**
* Creates a FieldAccessor for the given reflected field.
*
* Limitation: Field access via core reflection is only supported after
* java.lang.invoke completes initialization.
* java.lang.invoke initialization starts soon after System::initPhase1
* and method handles are ready for use when initPhase2 begins.
* During early VM startup (initPhase1), fields can be accessed directly
* from the VM or through JNI.
*/
static FieldAccessorImpl newFieldAccessor(Field field, boolean isReadOnly) {
if (!VM.isJavaLangInvokeInited()) {
throw new InternalError(field.getDeclaringClass().getName() + "::" + field.getName() +
" cannot be accessed reflectively before java.lang.invoke is initialized");
}
// ExceptionInInitializerError may be thrown during class initialization
// Ensure class initialized outside the invocation of method handle
// so that EIIE is propagated (not wrapped with ITE)
ensureClassInitialized(field.getDeclaringClass());
try {
// the declaring class of the field has been initialized
var getter = JLIA.unreflectField(field, false);
var setter = isReadOnly ? null : JLIA.unreflectField(field, true);
Class<?> type = field.getType();
if (type == Boolean.TYPE) {
return MethodHandleBooleanFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
} else if (type == Byte.TYPE) {
return MethodHandleByteFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
} else if (type == Short.TYPE) {
return MethodHandleShortFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
} else if (type == Character.TYPE) {
return MethodHandleCharacterFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
} else if (type == Integer.TYPE) {
return MethodHandleIntegerFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
} else if (type == Long.TYPE) {
return MethodHandleLongFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
} else if (type == Float.TYPE) {
return MethodHandleFloatFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
} else if (type == Double.TYPE) {
return MethodHandleDoubleFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
} else {
return MethodHandleObjectFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
}
} catch (IllegalAccessException e) {
throw new InternalError(e);
}
}
private static MethodHandle getDirectMethod(Method method, boolean callerSensitive) throws IllegalAccessException {
var mtype = methodType(method.getReturnType(), method.getParameterTypes());
var isStatic = Modifier.isStatic(method.getModifiers());
var dmh = isStatic ? JLIA.findStatic(method.getDeclaringClass(), method.getName(), mtype)
: JLIA.findVirtual(method.getDeclaringClass(), method.getName(), mtype);
if (callerSensitive) {
// the reflectiveInvoker for caller-sensitive method expects the same signature
// as Method::invoke i.e. (Object, Object[])Object
return makeTarget(dmh, isStatic, false);
}
return makeSpecializedTarget(dmh, isStatic, false);
}
/**
* Finds the method handle of a caller-sensitive adapter for the given
* caller-sensitive method. It has the same name as the given method
* with a trailing caller class parameter.
*
* @see CallerSensitiveAdapter
*/
private static MethodHandle findCallerSensitiveAdapter(Method method) throws IllegalAccessException {
String name = method.getName();
// append a Class parameter
MethodType mtype = methodType(method.getReturnType(), method.getParameterTypes())
.appendParameterTypes(Class.class);
boolean isStatic = Modifier.isStatic(method.getModifiers());
MethodHandle dmh = isStatic ? JLIA.findStatic(method.getDeclaringClass(), name, mtype)
: JLIA.findVirtual(method.getDeclaringClass(), name, mtype);
return dmh != null ? makeSpecializedTarget(dmh, isStatic, true) : null;
}
/**
* Transform the given dmh to a specialized target method handle.
*
* If {@code hasCallerParameter} parameter is true, transform the method handle
* of this method type: {@code (Object, Object[], Class)Object} for the default
* case.
*
* If {@code hasCallerParameter} parameter is false, transform the method handle
* of this method type: {@code (Object, Object[])Object} for the default case.
*
* If the number of formal arguments is small, use a method type specialized
* the number of formal arguments is 0, 1, and 2, for example, the method type
* of a static method with one argument can be: {@code (Object)Object}
*
* If it's a static method, there is no leading Object parameter.
*
* @apiNote
* This implementation avoids using MethodHandles::catchException to help
* cold startup performance since this combination is very costly to setup.
*
* @param dmh DirectMethodHandle
* @param isStatic whether given dmh represents static method or not
* @param hasCallerParameter whether given dmh represents a method with an
* additional caller Class parameter
* @return transformed dmh to be used as a target in direct method accessors
*/
static MethodHandle makeSpecializedTarget(MethodHandle dmh, boolean isStatic, boolean hasCallerParameter) {
MethodHandle target = dmh.asFixedArity();
// number of formal arguments to the original method (not the adapter)
// If it is a non-static method, it has a leading `this` argument.
// Also do not count the caller class argument
int paramCount = dmh.type().parameterCount() - (isStatic ? 0 : 1) - (hasCallerParameter ? 1 : 0);
MethodType mtype = specializedMethodType(isStatic, hasCallerParameter, paramCount);
if (paramCount > SPECIALIZED_PARAM_COUNT) {
int spreadArgPos = isStatic ? 0 : 1;
target = target.asSpreader(spreadArgPos, Object[].class, paramCount);
}
if (isStatic) {
// add leading 'this' parameter to static method which is then ignored
target = MethodHandles.dropArguments(target, 0, Object.class);
}
return target.asType(mtype);
}
// specialize for number of formal arguments <= 3 to avoid spreader
static final int SPECIALIZED_PARAM_COUNT = 3;
static MethodType specializedMethodType(boolean isStatic, boolean hasCallerParameter, int paramCount) {
return switch (paramCount) {
case 0 -> hasCallerParameter ? methodType(Object.class, Object.class, Class.class)
: genericMethodType(1);
case 1 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Class.class)
: genericMethodType(2);
case 2 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Object.class, Class.class)
: genericMethodType(3);
case 3 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Object.class, Object.class, Class.class)
: genericMethodType(4);
default -> hasCallerParameter ? methodType(Object.class, Object.class, Object[].class, Class.class)
: genericMethodType(1, true);
};
}
static MethodType specializedMethodTypeForConstructor(int paramCount) {
return switch (paramCount) {
case 0 -> genericMethodType(0);
case 1 -> genericMethodType(1);
case 2 -> genericMethodType(2);
case 3 -> genericMethodType(3);
default -> genericMethodType(0, true);
};
}
/**
* Transforms the given dmh into a target method handle with the method type
* {@code (Object, Object[])Object} or {@code (Object, Class, Object[])Object}
*/
static MethodHandle makeTarget(MethodHandle dmh, boolean isStatic, boolean hasCallerParameter) {
MethodType mtype = hasCallerParameter
? methodType(Object.class, Object.class, Object[].class, Class.class)
: genericMethodType(1, true);
// number of formal arguments
int paramCount = dmh.type().parameterCount() - (isStatic ? 0 : 1) - (hasCallerParameter ? 1 : 0);
int spreadArgPos = isStatic ? 0 : 1;
MethodHandle target = dmh.asFixedArity().asSpreader(spreadArgPos, Object[].class, paramCount);
if (isStatic) {
// add leading 'this' parameter to static method which is then ignored
target = MethodHandles.dropArguments(target, 0, Object.class);
}
return target.asType(mtype);
}
/**
* Ensures the given class is initialized. If this is called from <clinit>,
* this method returns but defc's class initialization is not completed.
*/
static void ensureClassInitialized(Class<?> defc) {
if (UNSAFE.shouldBeInitialized(defc)) {
UNSAFE.ensureClassInitialized(defc);
}
}
/*
* Returns true if NativeAccessor should be used.
*/
private static boolean useNativeAccessor(Executable member) {
if (!VM.isJavaLangInvokeInited())
return true;
if (Modifier.isNative(member.getModifiers()))
return true;
if (ReflectionFactory.useNativeAccessorOnly()) // for testing only
return true;
// MethodHandle::withVarargs on a member with varargs modifier bit set
// verifies that the last parameter of the member must be an array type.
// The JVMS does not require the last parameter descriptor of the method descriptor
// is an array type if the ACC_VARARGS flag is set in the access_flags item.
// Hence the reflection implementation does not check the last parameter type
// if ACC_VARARGS flag is set. Workaround this by invoking through
// the native accessor.
int paramCount = member.getParameterCount();
if (member.isVarArgs() &&
(paramCount == 0 || !(member.getParameterTypes()[paramCount-1].isArray()))) {
return true;
}
// A method handle cannot be created if its type has an arity >= 255
// as the method handle's invoke method consumes an extra argument
// of the method handle itself. Fall back to use the native implementation.
if (slotCount(member) >= MAX_JVM_ARITY) {
return true;
}
return false;
}
private static final int MAX_JVM_ARITY = 255; // this is mandated by the JVM spec.
/*
* Return number of slots of the given member.
* - long/double args counts for two argument slots
* - A non-static method consumes an extra argument for the object on which
* the method is called.
* - A constructor consumes an extra argument for the object which is being constructed.
*/
private static int slotCount(Executable member) {
int slots = 0;
Class<?>[] ptypes = member.getParameterTypes();
for (Class<?> ptype : ptypes) {
if (ptype == double.class || ptype == long.class) {
slots++;
}
}
return ptypes.length + slots +
(Modifier.isStatic(member.getModifiers()) ? 0 : 1);
}
/*
* Delay initializing these static fields until java.lang.invoke is fully initialized.
*/
static class LazyStaticHolder {
static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
}
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
}

View File

@ -0,0 +1,185 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class MethodHandleBooleanFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
boolean isStatic = Modifier.isStatic(field.getModifiers());
if (isStatic) {
getter = getter.asType(MethodType.methodType(boolean.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, boolean.class));
}
} else {
getter = getter.asType(MethodType.methodType(boolean.class, Object.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, Object.class, boolean.class));
}
}
return new MethodHandleBooleanFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
}
MethodHandleBooleanFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
super(field, getter, setter, isReadOnly, isStatic);
}
public Object get(Object obj) throws IllegalArgumentException {
return Boolean.valueOf(getBoolean(obj));
}
public boolean getBoolean(Object obj) throws IllegalArgumentException {
ensureObj(obj);
try {
if (isStatic()) {
return (boolean) getter.invokeExact();
} else {
return (boolean) getter.invokeExact(obj);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newGetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public byte getByte(Object obj) throws IllegalArgumentException {
throw newGetByteIllegalArgumentException();
}
public char getChar(Object obj) throws IllegalArgumentException {
throw newGetCharIllegalArgumentException();
}
public short getShort(Object obj) throws IllegalArgumentException {
throw newGetShortIllegalArgumentException();
}
public int getInt(Object obj) throws IllegalArgumentException {
throw newGetIntIllegalArgumentException();
}
public long getLong(Object obj) throws IllegalArgumentException {
throw newGetLongIllegalArgumentException();
}
public float getFloat(Object obj) throws IllegalArgumentException {
throw newGetFloatIllegalArgumentException();
}
public double getDouble(Object obj) throws IllegalArgumentException {
throw newGetDoubleIllegalArgumentException();
}
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
ensureObj(obj);
if (isReadOnly()) {
throwFinalFieldIllegalAccessException(value);
}
if (value == null) {
throwSetIllegalArgumentException(value);
}
if (value instanceof Boolean b) {
setBoolean(obj, b.booleanValue());
} else {
throwSetIllegalArgumentException(value);
}
}
public void setBoolean(Object obj, boolean z)
throws IllegalArgumentException, IllegalAccessException
{
ensureObj(obj);
if (isReadOnly()) {
throwFinalFieldIllegalAccessException(z);
}
try {
if (isStatic()) {
setter.invokeExact(z);
} else {
setter.invokeExact(obj, z);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newSetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public void setByte(Object obj, byte b)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(b);
}
public void setChar(Object obj, char c)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(c);
}
public void setShort(Object obj, short s)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(s);
}
public void setInt(Object obj, int i)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(i);
}
public void setLong(Object obj, long l)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(l);
}
public void setFloat(Object obj, float f)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(f);
}
public void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(d);
}
}

View File

@ -0,0 +1,184 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class MethodHandleByteFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
boolean isStatic = Modifier.isStatic(field.getModifiers());
if (isStatic) {
getter = getter.asType(MethodType.methodType(byte.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, byte.class));
}
} else {
getter = getter.asType(MethodType.methodType(byte.class, Object.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, Object.class, byte.class));
}
}
return new MethodHandleByteFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
}
MethodHandleByteFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
super(field, getter, setter, isReadOnly, isStatic);
}
public Object get(Object obj) throws IllegalArgumentException {
return Byte.valueOf(getByte(obj));
}
public boolean getBoolean(Object obj) throws IllegalArgumentException {
throw newGetBooleanIllegalArgumentException();
}
public byte getByte(Object obj) throws IllegalArgumentException {
try {
if (isStatic()) {
return (byte) getter.invokeExact();
} else {
return (byte) getter.invokeExact(obj);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newGetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public char getChar(Object obj) throws IllegalArgumentException {
throw newGetCharIllegalArgumentException();
}
public short getShort(Object obj) throws IllegalArgumentException {
return getByte(obj);
}
public int getInt(Object obj) throws IllegalArgumentException {
return getByte(obj);
}
public long getLong(Object obj) throws IllegalArgumentException {
return getByte(obj);
}
public float getFloat(Object obj) throws IllegalArgumentException {
return getByte(obj);
}
public double getDouble(Object obj) throws IllegalArgumentException {
return getByte(obj);
}
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
ensureObj(obj);
if (isReadOnly()) {
throwFinalFieldIllegalAccessException(value);
}
if (value == null) {
throwSetIllegalArgumentException(value);
}
if (value instanceof Byte b) {
setByte(obj, b.byteValue());
} else {
throwSetIllegalArgumentException(value);
}
}
public void setBoolean(Object obj, boolean z)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(z);
}
public void setByte(Object obj, byte b)
throws IllegalArgumentException, IllegalAccessException
{
ensureObj(obj);
if (isReadOnly()) {
throwFinalFieldIllegalAccessException(b);
}
try {
if (isStatic()) {
setter.invokeExact(b);
} else {
setter.invokeExact(obj, b);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newSetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public void setChar(Object obj, char c)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(c);
}
public void setShort(Object obj, short s)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(s);
}
public void setInt(Object obj, int i)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(i);
}
public void setLong(Object obj, long l)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(l);
}
public void setFloat(Object obj, float f)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(f);
}
public void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(d);
}
}

View File

@ -0,0 +1,184 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class MethodHandleCharacterFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
boolean isStatic = Modifier.isStatic(field.getModifiers());
if (isStatic) {
getter = getter.asType(MethodType.methodType(char.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, char.class));
}
} else {
getter = getter.asType(MethodType.methodType(char.class, Object.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, Object.class, char.class));
}
}
return new MethodHandleCharacterFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
}
MethodHandleCharacterFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
super(field, getter, setter, isReadOnly, isStatic);
}
public Object get(Object obj) throws IllegalArgumentException {
return Character.valueOf(getChar(obj));
}
public boolean getBoolean(Object obj) throws IllegalArgumentException {
throw newGetBooleanIllegalArgumentException();
}
public byte getByte(Object obj) throws IllegalArgumentException {
throw newGetByteIllegalArgumentException();
}
public char getChar(Object obj) throws IllegalArgumentException {
try {
if (isStatic()) {
return (char) getter.invokeExact();
} else {
return (char) getter.invokeExact(obj);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newGetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public short getShort(Object obj) throws IllegalArgumentException {
throw newGetShortIllegalArgumentException();
}
public int getInt(Object obj) throws IllegalArgumentException {
return getChar(obj);
}
public long getLong(Object obj) throws IllegalArgumentException {
return getChar(obj);
}
public float getFloat(Object obj) throws IllegalArgumentException {
return getChar(obj);
}
public double getDouble(Object obj) throws IllegalArgumentException {
return getChar(obj);
}
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(value);
}
if (value == null) {
throwSetIllegalArgumentException(value);
}
if (value instanceof Character c) {
setChar(obj, c.charValue());
} else {
throwSetIllegalArgumentException(value);
}
}
public void setBoolean(Object obj, boolean z)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(z);
}
public void setByte(Object obj, byte b)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(b);
}
public void setChar(Object obj, char c)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(c);
}
try {
if (isStatic()) {
setter.invokeExact(c);
} else {
setter.invokeExact(obj, c);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newSetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public void setShort(Object obj, short s)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(s);
}
public void setInt(Object obj, int i)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(i);
}
public void setLong(Object obj, long l)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(l);
}
public void setFloat(Object obj, float f)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(f);
}
public void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(d);
}
}

View File

@ -0,0 +1,203 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class MethodHandleDoubleFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
boolean isStatic = Modifier.isStatic(field.getModifiers());
if (isStatic) {
getter = getter.asType(MethodType.methodType(double.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, double.class));
}
} else {
getter = getter.asType(MethodType.methodType(double.class, Object.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, Object.class, double.class));
}
}
return new MethodHandleDoubleFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
}
MethodHandleDoubleFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
super(field, getter, setter, isReadOnly, isStatic);
}
public Object get(Object obj) throws IllegalArgumentException {
return Double.valueOf(getDouble(obj));
}
public boolean getBoolean(Object obj) throws IllegalArgumentException {
throw newGetBooleanIllegalArgumentException();
}
public byte getByte(Object obj) throws IllegalArgumentException {
throw newGetByteIllegalArgumentException();
}
public char getChar(Object obj) throws IllegalArgumentException {
throw newGetCharIllegalArgumentException();
}
public short getShort(Object obj) throws IllegalArgumentException {
throw newGetShortIllegalArgumentException();
}
public int getInt(Object obj) throws IllegalArgumentException {
throw newGetIntIllegalArgumentException();
}
public long getLong(Object obj) throws IllegalArgumentException {
throw newGetLongIllegalArgumentException();
}
public float getFloat(Object obj) throws IllegalArgumentException {
throw newGetFloatIllegalArgumentException();
}
public double getDouble(Object obj) throws IllegalArgumentException {
try {
if (isStatic()) {
return (double) getter.invokeExact();
} else {
return (double) getter.invokeExact(obj);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newGetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(value);
}
if (value == null) {
throwSetIllegalArgumentException(value);
}
if (value instanceof Byte b) {
setDouble(obj, b.byteValue());
}
else if (value instanceof Short s) {
setDouble(obj, s.shortValue());
}
else if (value instanceof Character c) {
setDouble(obj, c.charValue());
}
else if (value instanceof Integer i) {
setDouble(obj, i.intValue());
}
else if (value instanceof Long l) {
setDouble(obj, l.longValue());
}
else if (value instanceof Float f) {
setDouble(obj, f.floatValue());
}
else if (value instanceof Double d) {
setDouble(obj, d.doubleValue());
}
else {
throwSetIllegalArgumentException(value);
}
}
public void setBoolean(Object obj, boolean z)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(z);
}
public void setByte(Object obj, byte b)
throws IllegalArgumentException, IllegalAccessException
{
setDouble(obj, b);
}
public void setChar(Object obj, char c)
throws IllegalArgumentException, IllegalAccessException
{
setDouble(obj, c);
}
public void setShort(Object obj, short s)
throws IllegalArgumentException, IllegalAccessException
{
setDouble(obj, s);
}
public void setInt(Object obj, int i)
throws IllegalArgumentException, IllegalAccessException
{
setDouble(obj, i);
}
public void setLong(Object obj, long l)
throws IllegalArgumentException, IllegalAccessException
{
setDouble(obj, l);
}
public void setFloat(Object obj, float f)
throws IllegalArgumentException, IllegalAccessException
{
setDouble(obj, f);
}
public void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(d);
}
try {
if (isStatic()) {
setter.invokeExact(d);
} else {
setter.invokeExact(obj, d);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newSetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Field;
abstract class MethodHandleFieldAccessorImpl extends FieldAccessorImpl {
private static final int IS_READ_ONLY_BIT = 0x0001;
private static final int IS_STATIC_BIT = 0x0002;
private static final int NONZERO_BIT = 0x8000;
private final int fieldFlags;
protected final MethodHandle getter;
protected final MethodHandle setter;
protected MethodHandleFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
super(field);
this.fieldFlags = (isReadOnly ? IS_READ_ONLY_BIT : 0) |
(isStatic ? IS_STATIC_BIT : 0) |
NONZERO_BIT;
this.getter = getter;
this.setter = setter;
}
protected final boolean isReadOnly() {
return (fieldFlags & IS_READ_ONLY_BIT) == IS_READ_ONLY_BIT;
}
protected final boolean isStatic() {
return (fieldFlags & IS_STATIC_BIT) == IS_STATIC_BIT;
}
protected final void ensureObj(Object o) {
if (!isStatic()) {
// for compatibility, check the receiver object first
// throw NullPointerException if o is null
if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
throwSetIllegalArgumentException(o);
}
}
}
/**
* IllegalArgumentException because Field::get on the specified object, which
* is not an instance of the class or interface declaring the underlying method
*/
protected IllegalArgumentException newGetIllegalArgumentException(Class<?> type) {
return new IllegalArgumentException(getMessage(true, type.getName()));
}
/**
* IllegalArgumentException because Field::set on the specified object, which
* is not an instance of the class or interface declaring the underlying method
*/
protected IllegalArgumentException newSetIllegalArgumentException(Class<?> type) {
return new IllegalArgumentException(getMessage(false, type.getName()));
}
}

View File

@ -0,0 +1,200 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class MethodHandleFloatFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
boolean isStatic = Modifier.isStatic(field.getModifiers());
if (isStatic) {
getter = getter.asType(MethodType.methodType(float.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, float.class));
}
} else {
getter = getter.asType(MethodType.methodType(float.class, Object.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, Object.class, float.class));
}
}
return new MethodHandleFloatFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
}
MethodHandleFloatFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
super(field, getter, setter, isReadOnly, isStatic);
}
public Object get(Object obj) throws IllegalArgumentException {
return Float.valueOf(getFloat(obj));
}
public boolean getBoolean(Object obj) throws IllegalArgumentException {
throw newGetBooleanIllegalArgumentException();
}
public byte getByte(Object obj) throws IllegalArgumentException {
throw newGetByteIllegalArgumentException();
}
public char getChar(Object obj) throws IllegalArgumentException {
throw newGetCharIllegalArgumentException();
}
public short getShort(Object obj) throws IllegalArgumentException {
throw newGetShortIllegalArgumentException();
}
public int getInt(Object obj) throws IllegalArgumentException {
throw newGetIntIllegalArgumentException();
}
public long getLong(Object obj) throws IllegalArgumentException {
throw newGetLongIllegalArgumentException();
}
public float getFloat(Object obj) throws IllegalArgumentException {
try {
if (isStatic()) {
return (float) getter.invokeExact();
} else {
return (float) getter.invokeExact(obj);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newGetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public double getDouble(Object obj) throws IllegalArgumentException {
return getFloat(obj);
}
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(value);
}
if (value == null) {
throwSetIllegalArgumentException(value);
}
if (value instanceof Byte b) {
setFloat(obj, b.byteValue());
}
else if (value instanceof Short s) {
setFloat(obj, s.shortValue());
}
else if (value instanceof Character c) {
setFloat(obj, c.charValue());
}
else if (value instanceof Integer i) {
setFloat(obj, i.intValue());
}
else if (value instanceof Long l) {
setFloat(obj, l.longValue());
}
else if (value instanceof Float f) {
setFloat(obj, f.floatValue());
}
else {
throwSetIllegalArgumentException(value);
}
}
public void setBoolean(Object obj, boolean z)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(z);
}
public void setByte(Object obj, byte b)
throws IllegalArgumentException, IllegalAccessException
{
setFloat(obj, b);
}
public void setChar(Object obj, char c)
throws IllegalArgumentException, IllegalAccessException
{
setFloat(obj, c);
}
public void setShort(Object obj, short s)
throws IllegalArgumentException, IllegalAccessException
{
setFloat(obj, s);
}
public void setInt(Object obj, int i)
throws IllegalArgumentException, IllegalAccessException
{
setFloat(obj, i);
}
public void setLong(Object obj, long l)
throws IllegalArgumentException, IllegalAccessException
{
setFloat(obj, l);
}
public void setFloat(Object obj, float f)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(f);
}
try {
if (isStatic()) {
setter.invokeExact(f);
} else {
setter.invokeExact(obj, f);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newSetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(d);
}
}

View File

@ -0,0 +1,194 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class MethodHandleIntegerFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
boolean isStatic = Modifier.isStatic(field.getModifiers());
if (isStatic) {
getter = getter.asType(MethodType.methodType(int.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, int.class));
}
} else {
getter = getter.asType(MethodType.methodType(int.class, Object.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, Object.class, int.class));
}
}
return new MethodHandleIntegerFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
}
MethodHandleIntegerFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
super(field, getter, setter, isReadOnly, isStatic);
}
public Object get(Object obj) throws IllegalArgumentException {
return Integer.valueOf(getInt(obj));
}
public boolean getBoolean(Object obj) throws IllegalArgumentException {
throw newGetBooleanIllegalArgumentException();
}
public byte getByte(Object obj) throws IllegalArgumentException {
throw newGetByteIllegalArgumentException();
}
public char getChar(Object obj) throws IllegalArgumentException {
throw newGetCharIllegalArgumentException();
}
public short getShort(Object obj) throws IllegalArgumentException {
throw newGetShortIllegalArgumentException();
}
public int getInt(Object obj) throws IllegalArgumentException {
try {
if (isStatic()) {
return (int) getter.invokeExact();
} else {
return (int) getter.invokeExact(obj);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newGetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public long getLong(Object obj) throws IllegalArgumentException {
return getInt(obj);
}
public float getFloat(Object obj) throws IllegalArgumentException {
return getInt(obj);
}
public double getDouble(Object obj) throws IllegalArgumentException {
return getInt(obj);
}
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(value);
}
if (value == null) {
throwSetIllegalArgumentException(value);
}
if (value instanceof Byte b) {
setInt(obj, b.byteValue());
}
else if (value instanceof Short s) {
setInt(obj, s.shortValue());
}
else if (value instanceof Character c) {
setInt(obj, c.charValue());
}
else if (value instanceof Integer i) {
setInt(obj, i.intValue());
}
else {
throwSetIllegalArgumentException(value);
}
}
public void setBoolean(Object obj, boolean z)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(z);
}
public void setByte(Object obj, byte b)
throws IllegalArgumentException, IllegalAccessException
{
setInt(obj, b);
}
public void setChar(Object obj, char c)
throws IllegalArgumentException, IllegalAccessException
{
setInt(obj, c);
}
public void setShort(Object obj, short s)
throws IllegalArgumentException, IllegalAccessException
{
setInt(obj, s);
}
public void setInt(Object obj, int i)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(i);
}
try {
if (isStatic()) {
setter.invokeExact(i);
} else {
setter.invokeExact(obj, i);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newSetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public void setLong(Object obj, long l)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(l);
}
public void setFloat(Object obj, float f)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(f);
}
public void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(d);
}
}

View File

@ -0,0 +1,197 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class MethodHandleLongFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
boolean isStatic = Modifier.isStatic(field.getModifiers());
if (isStatic) {
getter = getter.asType(MethodType.methodType(long.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, long.class));
}
} else {
getter = getter.asType(MethodType.methodType(long.class, Object.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, Object.class, long.class));
}
}
return new MethodHandleLongFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
}
MethodHandleLongFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
super(field, getter, setter, isReadOnly, isStatic);
}
public Object get(Object obj) throws IllegalArgumentException {
return Long.valueOf(getLong(obj));
}
public boolean getBoolean(Object obj) throws IllegalArgumentException {
throw newGetBooleanIllegalArgumentException();
}
public byte getByte(Object obj) throws IllegalArgumentException {
throw newGetByteIllegalArgumentException();
}
public char getChar(Object obj) throws IllegalArgumentException {
throw newGetCharIllegalArgumentException();
}
public short getShort(Object obj) throws IllegalArgumentException {
throw newGetShortIllegalArgumentException();
}
public int getInt(Object obj) throws IllegalArgumentException {
throw newGetIntIllegalArgumentException();
}
public long getLong(Object obj) throws IllegalArgumentException {
try {
if (isStatic()) {
return (long) getter.invokeExact();
} else {
return (long) getter.invokeExact(obj);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newGetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public float getFloat(Object obj) throws IllegalArgumentException {
return getLong(obj);
}
public double getDouble(Object obj) throws IllegalArgumentException {
return getLong(obj);
}
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(value);
}
if (value == null) {
throwSetIllegalArgumentException(value);
}
if (value instanceof Byte b) {
setLong(obj, b.byteValue());
}
else if (value instanceof Short s) {
setLong(obj, s.shortValue());
}
else if (value instanceof Character c) {
setLong(obj, c.charValue());
}
else if (value instanceof Integer i) {
setLong(obj, i.intValue());
}
else if (value instanceof Long l) {
setLong(obj, l.longValue());
}
else {
throwSetIllegalArgumentException(value);
}
}
public void setBoolean(Object obj, boolean z)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(z);
}
public void setByte(Object obj, byte b)
throws IllegalArgumentException, IllegalAccessException
{
setLong(obj, b);
}
public void setChar(Object obj, char c)
throws IllegalArgumentException, IllegalAccessException
{
setLong(obj, c);
}
public void setShort(Object obj, short s)
throws IllegalArgumentException, IllegalAccessException
{
setLong(obj, s);
}
public void setInt(Object obj, int i)
throws IllegalArgumentException, IllegalAccessException
{
setLong(obj, i);
}
public void setLong(Object obj, long l)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(l);
}
try {
if (isStatic()) {
setter.invokeExact(l);
} else {
setter.invokeExact(obj, l);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newSetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public void setFloat(Object obj, float f)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(f);
}
public void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(d);
}
}

View File

@ -0,0 +1,167 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class MethodHandleObjectFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
boolean isStatic = Modifier.isStatic(field.getModifiers());
if (isStatic) {
getter = getter.asType(MethodType.methodType(Object.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, Object.class));
}
} else {
getter = getter.asType(MethodType.methodType(Object.class, Object.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, Object.class, Object.class));
}
}
return new MethodHandleObjectFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
}
MethodHandleObjectFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
super(field, getter, setter, isReadOnly, isStatic);
}
@Override
public Object get(Object obj) throws IllegalArgumentException {
try {
return isStatic() ? getter.invokeExact() : getter.invokeExact(obj);
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newGetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public boolean getBoolean(Object obj) throws IllegalArgumentException {
throw newGetBooleanIllegalArgumentException();
}
public byte getByte(Object obj) throws IllegalArgumentException {
throw newGetByteIllegalArgumentException();
}
public char getChar(Object obj) throws IllegalArgumentException {
throw newGetCharIllegalArgumentException();
}
public short getShort(Object obj) throws IllegalArgumentException {
throw newGetShortIllegalArgumentException();
}
public int getInt(Object obj) throws IllegalArgumentException {
throw newGetIntIllegalArgumentException();
}
public long getLong(Object obj) throws IllegalArgumentException {
throw newGetLongIllegalArgumentException();
}
public float getFloat(Object obj) throws IllegalArgumentException {
throw newGetFloatIllegalArgumentException();
}
public double getDouble(Object obj) throws IllegalArgumentException {
throw newGetDoubleIllegalArgumentException();
}
@Override
public void set(Object obj, Object value) throws IllegalAccessException {
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(value);
}
try {
if (isStatic()) {
setter.invokeExact(value);
} else {
setter.invokeExact(obj, value);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newSetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public void setBoolean(Object obj, boolean z)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(z);
}
public void setByte(Object obj, byte b)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(b);
}
public void setChar(Object obj, char c)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(c);
}
public void setShort(Object obj, short s)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(s);
}
public void setInt(Object obj, int i)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(i);
}
public void setLong(Object obj, long l)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(l);
}
public void setFloat(Object obj, float f)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(f);
}
public void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(d);
}
}

View File

@ -0,0 +1,188 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.reflect;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class MethodHandleShortFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
boolean isStatic = Modifier.isStatic(field.getModifiers());
if (isStatic) {
getter = getter.asType(MethodType.methodType(short.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, short.class));
}
} else {
getter = getter.asType(MethodType.methodType(short.class, Object.class));
if (setter != null) {
setter = setter.asType(MethodType.methodType(void.class, Object.class, short.class));
}
}
return new MethodHandleShortFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
}
MethodHandleShortFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
super(field, getter, setter, isReadOnly, isStatic);
}
public Object get(Object obj) throws IllegalArgumentException {
return Short.valueOf(getShort(obj));
}
public boolean getBoolean(Object obj) throws IllegalArgumentException {
throw newGetBooleanIllegalArgumentException();
}
public byte getByte(Object obj) throws IllegalArgumentException {
throw newGetByteIllegalArgumentException();
}
public char getChar(Object obj) throws IllegalArgumentException {
throw newGetCharIllegalArgumentException();
}
public short getShort(Object obj) throws IllegalArgumentException {
try {
if (isStatic()) {
return (short) getter.invokeExact();
} else {
return (short) getter.invokeExact(obj);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newGetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public int getInt(Object obj) throws IllegalArgumentException {
return getShort(obj);
}
public long getLong(Object obj) throws IllegalArgumentException {
return getShort(obj);
}
public float getFloat(Object obj) throws IllegalArgumentException {
return getShort(obj);
}
public double getDouble(Object obj) throws IllegalArgumentException {
return getShort(obj);
}
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(value);
}
if (value == null) {
throwSetIllegalArgumentException(value);
}
if (value instanceof Byte b) {
setShort(obj, b.byteValue());
}
else if (value instanceof Short s) {
setShort(obj, s.shortValue());
}
else {
throwSetIllegalArgumentException(value);
}
}
public void setBoolean(Object obj, boolean z)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(z);
}
public void setByte(Object obj, byte b)
throws IllegalArgumentException, IllegalAccessException
{
setShort(obj, b);
}
public void setChar(Object obj, char c)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(c);
}
public void setShort(Object obj, short s)
throws IllegalArgumentException, IllegalAccessException
{
if (isReadOnly()) {
ensureObj(obj); // throw NPE if obj is null on instance field
throwFinalFieldIllegalAccessException(s);
}
try {
if (isStatic()) {
setter.invokeExact(s);
} else {
setter.invokeExact(obj, s);
}
} catch (IllegalArgumentException|NullPointerException e) {
throw e;
} catch (ClassCastException e) {
throw newSetIllegalArgumentException(obj.getClass());
} catch (Throwable e) {
throw new InternalError(e);
}
}
public void setInt(Object obj, int i)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(i);
}
public void setLong(Object obj, long l)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(l);
}
public void setFloat(Object obj, float f)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(f);
}
public void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException
{
throwSetIllegalArgumentException(d);
}
}

View File

@ -51,7 +51,7 @@ class NativeConstructorAccessorImpl extends ConstructorAccessorImpl {
IllegalArgumentException,
InvocationTargetException
{
// We can't inflate a constructor belonging to a vm-anonymous class
// We can't inflate a constructor belonging to a hidden class
// because that kind of class can't be referred to by name, hence can't
// be found from the generated bytecode.
if (++numInvocations > ReflectionFactory.inflationThreshold()

View File

@ -49,7 +49,7 @@ class NativeMethodAccessorImpl extends MethodAccessorImpl {
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException
{
// We can't inflate methods belonging to vm-anonymous classes because
// We can't inflate methods belonging to hidden classes because
// that kind of class can't be referred to by name, hence can't be
// found from the generated bytecode.
if (++numInvocations > ReflectionFactory.inflationThreshold()

View File

@ -85,6 +85,15 @@ public class ReflectionFactory {
private static boolean noInflation = false;
private static int inflationThreshold = 15;
//
// New implementation uses direct invocation of method handles
private static final int METHOD_MH_ACCESSOR = 0x1;
private static final int FIELD_MH_ACCESSOR = 0x2;
private static final int ALL_MH_ACCESSORS = METHOD_MH_ACCESSOR|FIELD_MH_ACCESSOR;
private static int useDirectMethodHandle = ALL_MH_ACCESSORS;
private static boolean useNativeAccessorOnly = false; // for testing only
// true if deserialization constructor checking is disabled
private static boolean disableSerialConstructorChecks = false;
@ -137,24 +146,6 @@ public class ReflectionFactory {
return soleInstance;
}
/**
* Returns an alternate reflective Method instance for the given method
* intended for reflection to invoke, if present.
*
* A trusted method can define an alternate implementation for a method `foo`
* by defining a method named "reflected$foo" that will be invoked
* reflectively.
*/
private static Method findMethodForReflection(Method method) {
String altName = "reflected$" + method.getName();
try {
return method.getDeclaringClass()
.getDeclaredMethod(altName, method.getParameterTypes());
} catch (NoSuchMethodException ex) {
return null;
}
}
//--------------------------------------------------------------------------
//
// Routines used by java.lang.reflect
@ -181,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"));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -28,7 +28,6 @@ package jdk.internal.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.Stable;
/** Base class for jdk.internal.misc.Unsafe-based FieldAccessors. The
observation is that there are only nine types of fields from the
@ -40,168 +39,16 @@ import jdk.internal.vm.annotation.Stable;
abstract class UnsafeFieldAccessorImpl extends FieldAccessorImpl {
static final Unsafe unsafe = Unsafe.getUnsafe();
@Stable
protected final long fieldOffset;
protected final boolean isFinal;
UnsafeFieldAccessorImpl(Field field) {
super(field);
if (Modifier.isStatic(field.getModifiers()))
int mods = field.getModifiers();
this.isFinal = Modifier.isFinal(mods);
if (Modifier.isStatic(mods))
fieldOffset = unsafe.staticFieldOffset(field);
else
fieldOffset = unsafe.objectFieldOffset(field);
isFinal = Modifier.isFinal(field.getModifiers());
}
protected void ensureObj(Object o) {
// NOTE: will throw NullPointerException, as specified, if o is null
if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
throwSetIllegalArgumentException(o);
}
}
private String getQualifiedFieldName() {
return field.getDeclaringClass().getName() + "." +field.getName();
}
protected IllegalArgumentException newGetIllegalArgumentException(String type) {
return new IllegalArgumentException(
"Attempt to get "+field.getType().getName()+" field \"" +
getQualifiedFieldName() + "\" with illegal data type conversion to "+type
);
}
protected void throwFinalFieldIllegalAccessException(String attemptedType,
String attemptedValue)
throws IllegalAccessException {
throw new IllegalAccessException(getSetMessage(attemptedType, attemptedValue));
}
protected void throwFinalFieldIllegalAccessException(Object o) throws IllegalAccessException {
throwFinalFieldIllegalAccessException(o != null ? o.getClass().getName() : "", "");
}
protected void throwFinalFieldIllegalAccessException(boolean z) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("boolean", Boolean.toString(z));
}
protected void throwFinalFieldIllegalAccessException(char b) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("char", Character.toString(b));
}
protected void throwFinalFieldIllegalAccessException(byte b) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("byte", Byte.toString(b));
}
protected void throwFinalFieldIllegalAccessException(short b) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("short", Short.toString(b));
}
protected void throwFinalFieldIllegalAccessException(int i) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("int", Integer.toString(i));
}
protected void throwFinalFieldIllegalAccessException(long i) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("long", Long.toString(i));
}
protected void throwFinalFieldIllegalAccessException(float f) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("float", Float.toString(f));
}
protected void throwFinalFieldIllegalAccessException(double f) throws IllegalAccessException {
throwFinalFieldIllegalAccessException("double", Double.toString(f));
}
protected IllegalArgumentException newGetBooleanIllegalArgumentException() {
return newGetIllegalArgumentException("boolean");
}
protected IllegalArgumentException newGetByteIllegalArgumentException() {
return newGetIllegalArgumentException("byte");
}
protected IllegalArgumentException newGetCharIllegalArgumentException() {
return newGetIllegalArgumentException("char");
}
protected IllegalArgumentException newGetShortIllegalArgumentException() {
return newGetIllegalArgumentException("short");
}
protected IllegalArgumentException newGetIntIllegalArgumentException() {
return newGetIllegalArgumentException("int");
}
protected IllegalArgumentException newGetLongIllegalArgumentException() {
return newGetIllegalArgumentException("long");
}
protected IllegalArgumentException newGetFloatIllegalArgumentException() {
return newGetIllegalArgumentException("float");
}
protected IllegalArgumentException newGetDoubleIllegalArgumentException() {
return newGetIllegalArgumentException("double");
}
protected String getSetMessage(String attemptedType, String attemptedValue) {
String err = "Can not set";
if (Modifier.isStatic(field.getModifiers()))
err += " static";
if (isFinal)
err += " final";
err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " to ";
if (!attemptedValue.isEmpty()) {
err += "(" + attemptedType + ")" + attemptedValue;
} else {
if (!attemptedType.isEmpty())
err += attemptedType;
else
err += "null value";
}
return err;
}
protected void throwSetIllegalArgumentException(String attemptedType,
String attemptedValue) {
throw new IllegalArgumentException(getSetMessage(attemptedType,attemptedValue));
}
protected void throwSetIllegalArgumentException(Object o) {
throwSetIllegalArgumentException(o != null ? o.getClass().getName() : "", "");
}
protected void throwSetIllegalArgumentException(boolean b) {
throwSetIllegalArgumentException("boolean", Boolean.toString(b));
}
protected void throwSetIllegalArgumentException(byte b) {
throwSetIllegalArgumentException("byte", Byte.toString(b));
}
protected void throwSetIllegalArgumentException(char c) {
throwSetIllegalArgumentException("char", Character.toString(c));
}
protected void throwSetIllegalArgumentException(short s) {
throwSetIllegalArgumentException("short", Short.toString(s));
}
protected void throwSetIllegalArgumentException(int i) {
throwSetIllegalArgumentException("int", Integer.toString(i));
}
protected void throwSetIllegalArgumentException(long l) {
throwSetIllegalArgumentException("long", Long.toString(l));
}
protected void throwSetIllegalArgumentException(float f) {
throwSetIllegalArgumentException("float", Float.toString(f));
}
protected void throwSetIllegalArgumentException(double d) {
throwSetIllegalArgumentException("double", Double.toString(d));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,13 +26,8 @@
package jdk.internal.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.util.Set;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.Stable;
/** Base class for jdk.internal.misc.Unsafe-based FieldAccessors for static
fields. The observation is that there are only nine types of
fields from the standpoint of reflection code: the eight primitive
@ -46,7 +41,6 @@ abstract class UnsafeStaticFieldAccessorImpl extends UnsafeFieldAccessorImpl {
Set.of("base"));
}
@Stable
protected final Object base; // base
UnsafeStaticFieldAccessorImpl(Field field) {

View File

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

View File

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

View File

@ -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 {
/*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

View File

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

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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

View File

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

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

View 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) { }
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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);
}
}
}

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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