5050
5151/**
5252 * Information about the runtime class initialization state of a {@link DynamicHub class}, and
53- * {@link #initialize implementation} of class initialization according to the Java VM
54- * specification.
55- *
53+ * {@link #slowPath(ClassInitializationInfo, DynamicHub)} implementation} of class initialization
54+ * according to the Java VM specification.
55+ * <p>
5656 * The information is not directly stored in {@link DynamicHub} because 1) the class initialization
5757 * state is mutable while {@link DynamicHub} must be immutable, and 2) few classes require
5858 * initialization at runtime so factoring out the information reduces image size.
5959 */
6060@ InternalVMMethod
6161public final class ClassInitializationInfo {
6262
63- /** Singleton for classes that failed to link during image building. */
64- public static ClassInitializationInfo createFailedInfo () {
65- return new ClassInitializationInfo (InitState .InitializationError );
63+ private static final ClassInitializationInfo NO_INITIALIZER_NO_TRACKING = new ClassInitializationInfo (InitState .FullyInitialized , false , true , false );
64+ private static final ClassInitializationInfo INITIALIZED_NO_TRACKING = new ClassInitializationInfo (InitState .FullyInitialized , true , true , false );
65+
66+ public static ClassInitializationInfo createFailedInfo (boolean typeReachedTracked ) {
67+ return new ClassInitializationInfo (InitState .InitializationError , typeReachedTracked );
6668 }
6769
6870 /**
6971 * Singleton for classes that are already initialized during image building and do not need
7072 * class initialization at runtime, and don't have {@code <clinit>} methods.
7173 */
72- public static ClassInitializationInfo createNoInitializerInfo () {
73- return new ClassInitializationInfo (InitState .FullyInitialized , false , true );
74+ public static ClassInitializationInfo createNoInitializerInfo (boolean typeReachedTracked ) {
75+ if (typeReachedTracked ) {
76+ return new ClassInitializationInfo (InitState .FullyInitialized , false , true , typeReachedTracked );
77+ } else {
78+ return NO_INITIALIZER_NO_TRACKING ;
79+ }
7480 }
7581
7682 /**
7783 * For classes that are already initialized during image building and do not need class
7884 * initialization at runtime, but have {@code <clinit>} methods.
7985 */
80- public static ClassInitializationInfo createInitializedInfo () {
81- return new ClassInitializationInfo (InitState .FullyInitialized , true , true );
86+ public static ClassInitializationInfo createInitializedInfo (boolean typeReachedTracked ) {
87+ if (typeReachedTracked ) {
88+ return new ClassInitializationInfo (InitState .FullyInitialized , true , true , typeReachedTracked );
89+ } else {
90+ return INITIALIZED_NO_TRACKING ;
91+ }
92+ }
93+
94+ public boolean requiresSlowPath () {
95+ return slowPathRequired ;
8296 }
8397
8498 enum InitState {
@@ -95,6 +109,12 @@ enum InitState {
95109 InitializationError
96110 }
97111
112+ enum ReachedTriState {
113+ UNTRACKED ,
114+ NOT_REACHED ,
115+ REACHED
116+ }
117+
98118 interface ClassInitializerFunctionPointer extends CFunctionPointer {
99119 @ InvokeJavaFunctionPointer
100120 void invoke ();
@@ -106,6 +126,11 @@ interface ClassInitializerFunctionPointer extends CFunctionPointer {
106126 */
107127 private final FunctionPointerHolder classInitializer ;
108128
129+ /**
130+ * Describes whether this class has been reached at runtime.
131+ */
132+ private ReachedTriState reached ;
133+
109134 /**
110135 * The current initialization state.
111136 */
@@ -141,34 +166,37 @@ interface ClassInitializerFunctionPointer extends CFunctionPointer {
141166 private boolean hasInitializer ;
142167 private boolean buildTimeInitialized ;
143168
144- private boolean reached ;
169+ private boolean slowPathRequired ;
145170
146171 public boolean isReached () {
147- return reached ;
172+ return reached == ReachedTriState . REACHED ;
148173 }
149174
150175 @ Platforms (Platform .HOSTED_ONLY .class )
151- private ClassInitializationInfo (InitState initState , boolean hasInitializer , boolean buildTimeInitialized ) {
152- this (initState );
176+ private ClassInitializationInfo (InitState initState , boolean hasInitializer , boolean buildTimeInitialized , boolean typeReachedTracked ) {
177+ this (initState , typeReachedTracked );
153178 this .hasInitializer = hasInitializer ;
154179 this .buildTimeInitialized = buildTimeInitialized ;
155- this .reached = false ;
156180 }
157181
158182 @ Platforms (Platform .HOSTED_ONLY .class )
159- private ClassInitializationInfo (InitState initState ) {
183+ private ClassInitializationInfo (InitState initState , boolean typeReachedTracked ) {
160184 this .classInitializer = null ;
161185 this .initState = initState ;
162186 this .initLock = initState == InitState .FullyInitialized ? null : new ReentrantLock ();
163187 this .hasInitializer = true ;
188+ this .reached = typeReachedTracked ? ReachedTriState .NOT_REACHED : ReachedTriState .UNTRACKED ;
189+ this .slowPathRequired = reached != ReachedTriState .UNTRACKED || initState != InitState .FullyInitialized ;
164190 }
165191
166192 @ Platforms (Platform .HOSTED_ONLY .class )
167- public ClassInitializationInfo (CFunctionPointer classInitializer ) {
193+ public ClassInitializationInfo (CFunctionPointer classInitializer , boolean typeReachedTracked ) {
168194 this .classInitializer = classInitializer == null || classInitializer .isNull () ? null : new FunctionPointerHolder (classInitializer );
169195 this .initState = InitState .Linked ;
170196 this .initLock = new ReentrantLock ();
171197 this .hasInitializer = classInitializer != null ;
198+ this .reached = typeReachedTracked ? ReachedTriState .NOT_REACHED : ReachedTriState .UNTRACKED ;
199+ this .slowPathRequired = true ;
172200 }
173201
174202 public boolean hasInitializer () {
@@ -199,18 +227,41 @@ private boolean isReentrantInitialization(IsolateThread thread) {
199227 return thread .equal (initThread );
200228 }
201229
202- private static void reach (DynamicHub hub ) {
230+ /**
231+ * Marks the hierarchy of <code>hub</code> as reached. The concurrency primitives are not needed
232+ * as the external readers are always on the separate thread for which the ordering is not
233+ * relevant.
234+ * </p>
235+ * Note: as an optimization we can stop the traversal when a is already reached: The thread that
236+ * wrote the flag has continued up the hierarchy. No need to use memory ordering, in the worst
237+ * case the threads will mark parts of the hierarchy twice. This is guaranteed by the fact that
238+ * the thread that writes always continues up the hierarchy. If the thread reached a reached
239+ * type, that means there is at least one thread that climbed above the current point.
240+ */
241+ private static void markReached (DynamicHub hub ) {
203242 var current = hub ;
204243 do {
205- current .getClassInitializationInfo ().reached = true ;
244+ if (current .getClassInitializationInfo ().reached == ReachedTriState .REACHED ) {
245+ break ;
246+ }
247+
248+ if (current .getClassInitializationInfo ().reached != ReachedTriState .UNTRACKED ) {
249+ current .getClassInitializationInfo ().reached = ReachedTriState .REACHED ;
250+ }
206251 reachInterfaces (current );
207252 current = current .getSuperHub ();
208253 } while (current != null );
209254 }
210255
211256 private static void reachInterfaces (DynamicHub hub ) {
212257 for (DynamicHub superInterface : hub .getInterfaces ()) {
213- superInterface .getClassInitializationInfo ().reached = true ;
258+ if (superInterface .getClassInitializationInfo ().reached == ReachedTriState .REACHED ) {
259+ return ;
260+ }
261+
262+ if (hub .getClassInitializationInfo ().reached != ReachedTriState .UNTRACKED ) {
263+ superInterface .getClassInitializationInfo ().reached = ReachedTriState .REACHED ;
264+ }
214265 reachInterfaces (superInterface );
215266 }
216267 }
@@ -223,10 +274,15 @@ private static void reachInterfaces(DynamicHub hub) {
223274 * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.5
224275 */
225276 @ SubstrateForeignCallTarget (stubCallingConvention = true )
226- private static void initialize (ClassInitializationInfo info , DynamicHub hub ) {
277+ private static void slowPath (ClassInitializationInfo info , DynamicHub hub ) {
227278 IsolateThread self = CurrentIsolate .getCurrentThread ();
228279
229- reach (hub );
280+ /*
281+ * Types are marked as reached before any initialization is performed. Reason: the results
282+ * should be visible in class initializers of the whole hierarchy as they could use
283+ * reflection.
284+ */
285+ markReached (hub );
230286
231287 if (info .isInitialized ()) {
232288 return ;
@@ -431,6 +487,7 @@ private void setInitializationStateAndNotify(InitState state) {
431487 initLock .lock ();
432488 try {
433489 this .initState = state ;
490+ this .slowPathRequired = false ;
434491 this .initThread = WordFactory .nullPointer ();
435492 /* Make sure previous stores are all done, notably the initState. */
436493 Unsafe .getUnsafe ().storeFence ();
0 commit comments