4444import jdk .internal .access .JavaLangReflectAccess ;
4545import jdk .internal .access .SharedSecrets ;
4646import jdk .internal .misc .VM ;
47+ import jdk .internal .vm .annotation .Stable ;
4748import sun .security .action .GetPropertyAction ;
4849import sun .security .util .SecurityConstants ;
4950
6162
6263public 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