Skip to content

Commit 7feabee

Browse files
liachPeter Levart
authored andcommitted
8261407: ReflectionFactory.checkInitted() is not thread-safe
Co-authored-by: Peter Levart <[email protected]> Reviewed-by: dholmes, mchung, plevart
1 parent 58e1882 commit 7feabee

File tree

1 file changed

+89
-53
lines changed

1 file changed

+89
-53
lines changed

src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java

Lines changed: 89 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import jdk.internal.access.JavaLangReflectAccess;
4545
import jdk.internal.access.SharedSecrets;
4646
import jdk.internal.misc.VM;
47+
import jdk.internal.vm.annotation.Stable;
4748
import sun.security.action.GetPropertyAction;
4849
import sun.security.util.SecurityConstants;
4950

@@ -61,42 +62,12 @@
6162

6263
public class ReflectionFactory {
6364

64-
private static boolean initted = false;
6565
private static final ReflectionFactory soleInstance = new ReflectionFactory();
6666

6767

6868
/* Method for static class initializer <clinit>, or null */
6969
private static volatile Method hasStaticInitializerMethod;
7070

71-
//
72-
// "Inflation" mechanism. Loading bytecodes to implement
73-
// Method.invoke() and Constructor.newInstance() currently costs
74-
// 3-4x more than an invocation via native code for the first
75-
// invocation (though subsequent invocations have been benchmarked
76-
// to be over 20x faster). Unfortunately this cost increases
77-
// startup time for certain applications that use reflection
78-
// intensively (but only once per class) to bootstrap themselves.
79-
// To avoid this penalty we reuse the existing JVM entry points
80-
// for the first few invocations of Methods and Constructors and
81-
// then switch to the bytecode-based implementations.
82-
//
83-
// Package-private to be accessible to NativeMethodAccessorImpl
84-
// and NativeConstructorAccessorImpl
85-
private static boolean noInflation = false;
86-
private static int inflationThreshold = 15;
87-
88-
//
89-
// New implementation uses direct invocation of method handles
90-
private static final int METHOD_MH_ACCESSOR = 0x1;
91-
private static final int FIELD_MH_ACCESSOR = 0x2;
92-
private static final int ALL_MH_ACCESSORS = METHOD_MH_ACCESSOR|FIELD_MH_ACCESSOR;
93-
94-
private static int useDirectMethodHandle = ALL_MH_ACCESSORS;
95-
private static boolean useNativeAccessorOnly = false; // for testing only
96-
97-
// true if deserialization constructor checking is disabled
98-
private static boolean disableSerialConstructorChecks = false;
99-
10071
private final JavaLangReflectAccess langReflectAccess;
10172
private ReflectionFactory() {
10273
this.langReflectAccess = SharedSecrets.getJavaLangReflectAccess();
@@ -160,8 +131,6 @@ public static ReflectionFactory getReflectionFactory() {
160131
* @param override true if caller has overridden accessibility
161132
*/
162133
public FieldAccessor newFieldAccessor(Field field, boolean override) {
163-
checkInitted();
164-
165134
Field root = langReflectAccess.getRoot(field);
166135
if (root != null) {
167136
// FieldAccessor will use the root unless the modifiers have
@@ -180,8 +149,6 @@ public FieldAccessor newFieldAccessor(Field field, boolean override) {
180149
}
181150

182151
public MethodAccessor newMethodAccessor(Method method, boolean callerSensitive) {
183-
checkInitted();
184-
185152
// use the root Method that will not cache caller class
186153
Method root = langReflectAccess.getRoot(method);
187154
if (root != null) {
@@ -191,7 +158,7 @@ public MethodAccessor newMethodAccessor(Method method, boolean callerSensitive)
191158
if (useMethodHandleAccessor()) {
192159
return MethodHandleAccessorFactory.newMethodAccessor(method, callerSensitive);
193160
} else {
194-
if (noInflation && !method.getDeclaringClass().isHidden()) {
161+
if (noInflation() && !method.getDeclaringClass().isHidden()) {
195162
return generateMethodAccessor(method);
196163
} else {
197164
NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
@@ -215,8 +182,6 @@ static MethodAccessorImpl generateMethodAccessor(Method method) {
215182
}
216183

217184
public ConstructorAccessor newConstructorAccessor(Constructor<?> c) {
218-
checkInitted();
219-
220185
Class<?> declaringClass = c.getDeclaringClass();
221186
if (Modifier.isAbstract(declaringClass.getModifiers())) {
222187
return new InstantiationExceptionConstructorAccessorImpl(null);
@@ -242,7 +207,7 @@ public ConstructorAccessor newConstructorAccessor(Constructor<?> c) {
242207
return new BootstrapConstructorAccessorImpl(c);
243208
}
244209

245-
if (noInflation && !c.getDeclaringClass().isHidden()) {
210+
if (noInflation() && !c.getDeclaringClass().isHidden()) {
246211
return new MethodAccessorGenerator().
247212
generateConstructor(c.getDeclaringClass(),
248213
c.getParameterTypes(),
@@ -430,7 +395,7 @@ public final Constructor<?> newConstructorForSerialization(Class<?> cl) {
430395
while (Serializable.class.isAssignableFrom(initCl)) {
431396
Class<?> prev = initCl;
432397
if ((initCl = initCl.getSuperclass()) == null ||
433-
(!disableSerialConstructorChecks && !superHasAccessibleConstructor(prev))) {
398+
(!disableSerialConstructorChecks() && !superHasAccessibleConstructor(prev))) {
434399
return null;
435400
}
436401
}
@@ -623,41 +588,108 @@ public final Constructor<OptionalDataException> newOptionalDataExceptionForSeria
623588
// Internals only below this point
624589
//
625590

591+
// Package-private to be accessible to NativeMethodAccessorImpl
592+
// and NativeConstructorAccessorImpl
626593
static int inflationThreshold() {
627-
return inflationThreshold;
594+
return config().inflationThreshold;
628595
}
629596

630597
static boolean noInflation() {
631-
return noInflation;
598+
return config().noInflation;
632599
}
633600

634601
static boolean useMethodHandleAccessor() {
635-
return (useDirectMethodHandle & METHOD_MH_ACCESSOR) == METHOD_MH_ACCESSOR;
602+
return (config().useDirectMethodHandle & METHOD_MH_ACCESSOR) == METHOD_MH_ACCESSOR;
636603
}
637604

638605
static boolean useFieldHandleAccessor() {
639-
return (useDirectMethodHandle & FIELD_MH_ACCESSOR) == FIELD_MH_ACCESSOR;
606+
return (config().useDirectMethodHandle & FIELD_MH_ACCESSOR) == FIELD_MH_ACCESSOR;
640607
}
641608

642609
static boolean useNativeAccessorOnly() {
643-
return useNativeAccessorOnly;
610+
return config().useNativeAccessorOnly;
644611
}
645612

646-
/** We have to defer full initialization of this class until after
647-
the static initializer is run since java.lang.reflect.Method's
648-
static initializer (more properly, that for
649-
java.lang.reflect.AccessibleObject) causes this class's to be
650-
run, before the system properties are set up. */
651-
private static void checkInitted() {
652-
if (initted) return;
613+
private static boolean disableSerialConstructorChecks() {
614+
return config().disableSerialConstructorChecks;
615+
}
616+
617+
// New implementation uses direct invocation of method handles
618+
private static final int METHOD_MH_ACCESSOR = 0x1;
619+
private static final int FIELD_MH_ACCESSOR = 0x2;
620+
private static final int ALL_MH_ACCESSORS = METHOD_MH_ACCESSOR | FIELD_MH_ACCESSOR;
621+
622+
/**
623+
* The configuration is lazily initialized after the module system is initialized. The
624+
* default config would be used before the proper config is loaded.
625+
*
626+
* The static initializer of ReflectionFactory is run before the system properties are set up.
627+
* The class initialization is caused by the class initialization of java.lang.reflect.Method
628+
* (more properly, caused by the class initialization for java.lang.reflect.AccessibleObject)
629+
* that happens very early VM startup, initPhase1.
630+
*/
631+
private static @Stable Config config;
632+
633+
// "Inflation" mechanism. Loading bytecodes to implement
634+
// Method.invoke() and Constructor.newInstance() currently costs
635+
// 3-4x more than an invocation via native code for the first
636+
// invocation (though subsequent invocations have been benchmarked
637+
// to be over 20x faster). Unfortunately this cost increases
638+
// startup time for certain applications that use reflection
639+
// intensively (but only once per class) to bootstrap themselves.
640+
// To avoid this penalty we reuse the existing JVM entry points
641+
// for the first few invocations of Methods and Constructors and
642+
// then switch to the bytecode-based implementations.
643+
644+
private static final Config DEFAULT_CONFIG = new Config(false, // noInflation
645+
15, // inflationThreshold
646+
ALL_MH_ACCESSORS, // useDirectMethodHandle
647+
false, // useNativeAccessorOnly
648+
false); // disableSerialConstructorChecks
649+
650+
/**
651+
* The configurations for the reflection factory. Configurable via
652+
* system properties but only available after ReflectionFactory is
653+
* loaded during early VM startup.
654+
*
655+
* Note that the default implementations of the object methods of
656+
* this Config record (toString, equals, hashCode) use indy,
657+
* which is available to use only after initPhase1. These methods
658+
* are currently not called, but should they be needed, a workaround
659+
* is to override them.
660+
*/
661+
private record Config(boolean noInflation,
662+
int inflationThreshold,
663+
int useDirectMethodHandle,
664+
boolean useNativeAccessorOnly,
665+
boolean disableSerialConstructorChecks) {
666+
}
667+
668+
private static Config config() {
669+
Config c = config;
670+
if (c != null) {
671+
return c;
672+
}
653673

654674
// Defer initialization until module system is initialized so as
655675
// to avoid inflation and spinning bytecode in unnamed modules
656676
// during early startup.
657677
if (!VM.isModuleSystemInited()) {
658-
return;
678+
return DEFAULT_CONFIG;
659679
}
660680

681+
return config = loadConfig();
682+
}
683+
684+
private static Config loadConfig() {
685+
assert VM.isModuleSystemInited();
686+
687+
boolean noInflation = DEFAULT_CONFIG.noInflation;
688+
int inflationThreshold = DEFAULT_CONFIG.inflationThreshold;
689+
int useDirectMethodHandle = DEFAULT_CONFIG.useDirectMethodHandle;
690+
boolean useNativeAccessorOnly = DEFAULT_CONFIG.useNativeAccessorOnly;
691+
boolean disableSerialConstructorChecks = DEFAULT_CONFIG.disableSerialConstructorChecks;
692+
661693
Properties props = GetPropertyAction.privilegedGetProperties();
662694
String val = props.getProperty("sun.reflect.noInflation");
663695
if (val != null && val.equals("true")) {
@@ -690,7 +722,11 @@ private static void checkInitted() {
690722
disableSerialConstructorChecks =
691723
"true".equals(props.getProperty("jdk.disableSerialConstructorChecks"));
692724

693-
initted = true;
725+
return new Config(noInflation,
726+
inflationThreshold,
727+
useDirectMethodHandle,
728+
useNativeAccessorOnly,
729+
disableSerialConstructorChecks);
694730
}
695731

696732
/**

0 commit comments

Comments
 (0)