8261407: ReflectionFactory.checkInitted() is not thread-safe
Co-authored-by: Peter Levart <plevart@openjdk.org> Reviewed-by: dholmes, mchung, plevart
This commit is contained in:
parent
58e1882f3c
commit
7feabee426
@ -44,6 +44,7 @@ import java.util.Properties;
|
||||
import jdk.internal.access.JavaLangReflectAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.security.util.SecurityConstants;
|
||||
|
||||
@ -61,42 +62,12 @@ import sun.security.util.SecurityConstants;
|
||||
|
||||
public class ReflectionFactory {
|
||||
|
||||
private static boolean initted = false;
|
||||
private static final ReflectionFactory soleInstance = new ReflectionFactory();
|
||||
|
||||
|
||||
/* Method for static class initializer <clinit>, or null */
|
||||
private static volatile Method hasStaticInitializerMethod;
|
||||
|
||||
//
|
||||
// "Inflation" mechanism. Loading bytecodes to implement
|
||||
// Method.invoke() and Constructor.newInstance() currently costs
|
||||
// 3-4x more than an invocation via native code for the first
|
||||
// invocation (though subsequent invocations have been benchmarked
|
||||
// to be over 20x faster). Unfortunately this cost increases
|
||||
// startup time for certain applications that use reflection
|
||||
// intensively (but only once per class) to bootstrap themselves.
|
||||
// To avoid this penalty we reuse the existing JVM entry points
|
||||
// for the first few invocations of Methods and Constructors and
|
||||
// then switch to the bytecode-based implementations.
|
||||
//
|
||||
// Package-private to be accessible to NativeMethodAccessorImpl
|
||||
// and NativeConstructorAccessorImpl
|
||||
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;
|
||||
|
||||
private final JavaLangReflectAccess langReflectAccess;
|
||||
private ReflectionFactory() {
|
||||
this.langReflectAccess = SharedSecrets.getJavaLangReflectAccess();
|
||||
@ -160,8 +131,6 @@ public class ReflectionFactory {
|
||||
* @param override true if caller has overridden accessibility
|
||||
*/
|
||||
public FieldAccessor newFieldAccessor(Field field, boolean override) {
|
||||
checkInitted();
|
||||
|
||||
Field root = langReflectAccess.getRoot(field);
|
||||
if (root != null) {
|
||||
// FieldAccessor will use the root unless the modifiers have
|
||||
@ -180,8 +149,6 @@ public class ReflectionFactory {
|
||||
}
|
||||
|
||||
public MethodAccessor newMethodAccessor(Method method, boolean callerSensitive) {
|
||||
checkInitted();
|
||||
|
||||
// use the root Method that will not cache caller class
|
||||
Method root = langReflectAccess.getRoot(method);
|
||||
if (root != null) {
|
||||
@ -191,7 +158,7 @@ public class ReflectionFactory {
|
||||
if (useMethodHandleAccessor()) {
|
||||
return MethodHandleAccessorFactory.newMethodAccessor(method, callerSensitive);
|
||||
} else {
|
||||
if (noInflation && !method.getDeclaringClass().isHidden()) {
|
||||
if (noInflation() && !method.getDeclaringClass().isHidden()) {
|
||||
return generateMethodAccessor(method);
|
||||
} else {
|
||||
NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
|
||||
@ -215,8 +182,6 @@ public class ReflectionFactory {
|
||||
}
|
||||
|
||||
public ConstructorAccessor newConstructorAccessor(Constructor<?> c) {
|
||||
checkInitted();
|
||||
|
||||
Class<?> declaringClass = c.getDeclaringClass();
|
||||
if (Modifier.isAbstract(declaringClass.getModifiers())) {
|
||||
return new InstantiationExceptionConstructorAccessorImpl(null);
|
||||
@ -242,7 +207,7 @@ public class ReflectionFactory {
|
||||
return new BootstrapConstructorAccessorImpl(c);
|
||||
}
|
||||
|
||||
if (noInflation && !c.getDeclaringClass().isHidden()) {
|
||||
if (noInflation() && !c.getDeclaringClass().isHidden()) {
|
||||
return new MethodAccessorGenerator().
|
||||
generateConstructor(c.getDeclaringClass(),
|
||||
c.getParameterTypes(),
|
||||
@ -430,7 +395,7 @@ public class ReflectionFactory {
|
||||
while (Serializable.class.isAssignableFrom(initCl)) {
|
||||
Class<?> prev = initCl;
|
||||
if ((initCl = initCl.getSuperclass()) == null ||
|
||||
(!disableSerialConstructorChecks && !superHasAccessibleConstructor(prev))) {
|
||||
(!disableSerialConstructorChecks() && !superHasAccessibleConstructor(prev))) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -623,41 +588,108 @@ public class ReflectionFactory {
|
||||
// Internals only below this point
|
||||
//
|
||||
|
||||
// Package-private to be accessible to NativeMethodAccessorImpl
|
||||
// and NativeConstructorAccessorImpl
|
||||
static int inflationThreshold() {
|
||||
return inflationThreshold;
|
||||
return config().inflationThreshold;
|
||||
}
|
||||
|
||||
static boolean noInflation() {
|
||||
return noInflation;
|
||||
return config().noInflation;
|
||||
}
|
||||
|
||||
static boolean useMethodHandleAccessor() {
|
||||
return (useDirectMethodHandle & METHOD_MH_ACCESSOR) == METHOD_MH_ACCESSOR;
|
||||
return (config().useDirectMethodHandle & METHOD_MH_ACCESSOR) == METHOD_MH_ACCESSOR;
|
||||
}
|
||||
|
||||
static boolean useFieldHandleAccessor() {
|
||||
return (useDirectMethodHandle & FIELD_MH_ACCESSOR) == FIELD_MH_ACCESSOR;
|
||||
return (config().useDirectMethodHandle & FIELD_MH_ACCESSOR) == FIELD_MH_ACCESSOR;
|
||||
}
|
||||
|
||||
static boolean useNativeAccessorOnly() {
|
||||
return useNativeAccessorOnly;
|
||||
return config().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
|
||||
java.lang.reflect.AccessibleObject) causes this class's to be
|
||||
run, before the system properties are set up. */
|
||||
private static void checkInitted() {
|
||||
if (initted) return;
|
||||
private static boolean disableSerialConstructorChecks() {
|
||||
return config().disableSerialConstructorChecks;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* The configuration is lazily initialized after the module system is initialized. The
|
||||
* default config would be used before the proper config is loaded.
|
||||
*
|
||||
* The static initializer of ReflectionFactory is run before the system properties are set up.
|
||||
* The class initialization is caused by the class initialization of java.lang.reflect.Method
|
||||
* (more properly, caused by the class initialization for java.lang.reflect.AccessibleObject)
|
||||
* that happens very early VM startup, initPhase1.
|
||||
*/
|
||||
private static @Stable Config config;
|
||||
|
||||
// "Inflation" mechanism. Loading bytecodes to implement
|
||||
// Method.invoke() and Constructor.newInstance() currently costs
|
||||
// 3-4x more than an invocation via native code for the first
|
||||
// invocation (though subsequent invocations have been benchmarked
|
||||
// to be over 20x faster). Unfortunately this cost increases
|
||||
// startup time for certain applications that use reflection
|
||||
// intensively (but only once per class) to bootstrap themselves.
|
||||
// To avoid this penalty we reuse the existing JVM entry points
|
||||
// for the first few invocations of Methods and Constructors and
|
||||
// then switch to the bytecode-based implementations.
|
||||
|
||||
private static final Config DEFAULT_CONFIG = new Config(false, // noInflation
|
||||
15, // inflationThreshold
|
||||
ALL_MH_ACCESSORS, // useDirectMethodHandle
|
||||
false, // useNativeAccessorOnly
|
||||
false); // disableSerialConstructorChecks
|
||||
|
||||
/**
|
||||
* The configurations for the reflection factory. Configurable via
|
||||
* system properties but only available after ReflectionFactory is
|
||||
* loaded during early VM startup.
|
||||
*
|
||||
* Note that the default implementations of the object methods of
|
||||
* this Config record (toString, equals, hashCode) use indy,
|
||||
* which is available to use only after initPhase1. These methods
|
||||
* are currently not called, but should they be needed, a workaround
|
||||
* is to override them.
|
||||
*/
|
||||
private record Config(boolean noInflation,
|
||||
int inflationThreshold,
|
||||
int useDirectMethodHandle,
|
||||
boolean useNativeAccessorOnly,
|
||||
boolean disableSerialConstructorChecks) {
|
||||
}
|
||||
|
||||
private static Config config() {
|
||||
Config c = config;
|
||||
if (c != null) {
|
||||
return c;
|
||||
}
|
||||
|
||||
// Defer initialization until module system is initialized so as
|
||||
// to avoid inflation and spinning bytecode in unnamed modules
|
||||
// during early startup.
|
||||
if (!VM.isModuleSystemInited()) {
|
||||
return;
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
return config = loadConfig();
|
||||
}
|
||||
|
||||
private static Config loadConfig() {
|
||||
assert VM.isModuleSystemInited();
|
||||
|
||||
boolean noInflation = DEFAULT_CONFIG.noInflation;
|
||||
int inflationThreshold = DEFAULT_CONFIG.inflationThreshold;
|
||||
int useDirectMethodHandle = DEFAULT_CONFIG.useDirectMethodHandle;
|
||||
boolean useNativeAccessorOnly = DEFAULT_CONFIG.useNativeAccessorOnly;
|
||||
boolean disableSerialConstructorChecks = DEFAULT_CONFIG.disableSerialConstructorChecks;
|
||||
|
||||
Properties props = GetPropertyAction.privilegedGetProperties();
|
||||
String val = props.getProperty("sun.reflect.noInflation");
|
||||
if (val != null && val.equals("true")) {
|
||||
@ -690,7 +722,11 @@ public class ReflectionFactory {
|
||||
disableSerialConstructorChecks =
|
||||
"true".equals(props.getProperty("jdk.disableSerialConstructorChecks"));
|
||||
|
||||
initted = true;
|
||||
return new Config(noInflation,
|
||||
inflationThreshold,
|
||||
useDirectMethodHandle,
|
||||
useNativeAccessorOnly,
|
||||
disableSerialConstructorChecks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user