4444import java .util .function .Function ;
4545import java .util .stream .Collectors ;
4646
47- import jdk .graal .compiler .core .common .ContextClassLoaderScope ;
48- import org .graalvm .nativeimage .libgraal .hosted .LibGraalLoader ;
4947import org .graalvm .collections .EconomicSet ;
5048import org .graalvm .nativeimage .ImageSingletons ;
5149import org .graalvm .nativeimage .impl .RuntimeClassInitializationSupport ;
5250import org .graalvm .nativeimage .impl .clinit .ClassInitializationTracking ;
51+ import org .graalvm .nativeimage .libgraal .hosted .LibGraalLoader ;
5352
53+ import com .oracle .graal .pointsto .BigBang ;
5454import com .oracle .graal .pointsto .infrastructure .OriginalClassProvider ;
5555import com .oracle .graal .pointsto .meta .AnalysisType ;
5656import com .oracle .graal .pointsto .meta .BaseLayerType ;
5757import com .oracle .graal .pointsto .reports .ReportUtils ;
5858import com .oracle .svm .core .SubstrateOptions ;
59+ import com .oracle .svm .core .imagelayer .ImageLayerBuildingSupport ;
5960import com .oracle .svm .core .option .AccumulatingLocatableMultiOptionValue ;
6061import com .oracle .svm .core .option .SubstrateOptionsParser ;
6162import com .oracle .svm .core .util .UserError ;
6566import com .oracle .svm .util .LogUtils ;
6667import com .oracle .svm .util .ModuleSupport ;
6768
69+ import jdk .graal .compiler .core .common .ContextClassLoaderScope ;
6870import jdk .graal .compiler .java .LambdaUtils ;
6971import jdk .internal .misc .Unsafe ;
7072import jdk .vm .ci .meta .MetaAccessProvider ;
7375/**
7476 * The core class for deciding whether a class should be initialized during image building or class
7577 * initialization should be delayed to runtime.
78+ * <p>
79+ * The initialization kind for all classes is encoded in the two registries:
80+ * {@link #classInitializationConfiguration}, the user-configured initialization state, and
81+ * {@link #classInitKinds}, the actual computed initialization state.
82+ * <p>
83+ * If for example the configured initialization kind, as registered in
84+ * {@link #classInitializationConfiguration}, is {@link InitKind#BUILD_TIME} but invoking
85+ * {@link #ensureClassInitialized(Class, boolean)} results in an error, e.g., a
86+ * {@link NoClassDefFoundError}, then the actual initialization kind registered in
87+ * {@link #classInitKinds} may be {@link InitKind#RUN_TIME} depending on the error resolution policy
88+ * dictated by {@link LinkAtBuildTimeSupport#linkAtBuildTime(Class)}.
89+ * <p>
90+ * Classes with a simulated class initializer are neither registered as initialized at
91+ * {@link InitKind#RUN_TIME} nor {@link InitKind#BUILD_TIME}. Instead
92+ * {@link SimulateClassInitializerSupport} queries their initialization state to decide if
93+ * simulation should be tried. If a class has a computed {@link InitKind#BUILD_TIME} initialization
94+ * kind, i.e., its {@link AnalysisType#isInitialized()} returns true, simulation is skipped since
95+ * the type is already considered as starting out as initialized at image run time (see
96+ * {@link SimulateClassInitializerSupport#trySimulateClassInitializer(BigBang, AnalysisType)}). If a
97+ * class was explicitly configured as {@link InitKind#RUN_TIME} initialized this will prevent it
98+ * from being simulated.
99+ * <p>
100+ * There are some similarities and differences between simulated and build-time initialized classes.
101+ * At image execution time they both start out as initialized: there are no run-time class
102+ * initialization checks and the class initializer itself is not even present, it was not AOT
103+ * compiled. However, for a simulated class its initialization status in the hosting VM that runs
104+ * the image generator does not matter; it may or may not have been initialized. Whereas, a
105+ * build-time initialized class is by definition initialized in the hosting VM. Consequently, the
106+ * static fields of a simulated class reference image heap values computed by the class initializer
107+ * simulation, but they do not correspond to hosted objects. In contrast, static fields of a
108+ * build-time initialized class reference image heap values that were copied from the corresponding
109+ * fields in the hosting VM.
76110 */
77111public class ClassInitializationSupport implements RuntimeClassInitializationSupport {
78112
@@ -125,7 +159,29 @@ public ClassInitializationSupport(MetaAccessProvider metaAccess, ImageClassLoade
125159 this .loader = loader ;
126160 }
127161
128- public void setConfigurationSealed (boolean sealed ) {
162+ /**
163+ * Seal the configuration, blocking if another thread is trying to seal the configuration or an
164+ * unsealed-configuration window is currently open in another thread.
165+ */
166+ public synchronized void sealConfiguration () {
167+ setConfigurationSealed (true );
168+ }
169+
170+ /**
171+ * Run the action in an unsealed-configuration window, blocking if another thread is trying to
172+ * seal the configuration or an unsealed-configuration window is currently open in another
173+ * thread. The window is reentrant, i.e., it will not block the thread that opened the window
174+ * from trying to reenter the window. Note that if the configuration was not sealed when the
175+ * window was opened this will not affect the seal status.
176+ */
177+ public synchronized void withUnsealedConfiguration (Runnable action ) {
178+ var previouslySealed = configurationSealed ;
179+ setConfigurationSealed (false );
180+ action .run ();
181+ setConfigurationSealed (previouslySealed );
182+ }
183+
184+ private void setConfigurationSealed (boolean sealed ) {
129185 configurationSealed = sealed ;
130186 if (configurationSealed && ClassInitializationOptions .PrintClassInitialization .getValue ()) {
131187 List <ClassOrPackageConfig > allConfigs = classInitializationConfiguration .allConfigs ();
@@ -170,7 +226,7 @@ Set<Class<?>> classesWithKind(InitKind kind) {
170226 */
171227 public boolean maybeInitializeAtBuildTime (ResolvedJavaType type ) {
172228 if (type instanceof AnalysisType analysisType && analysisType .getWrapped () instanceof BaseLayerType baseLayerType ) {
173- return baseLayerType .initializedAtBuildTime ();
229+ return baseLayerType .isInitialized ();
174230 }
175231 return maybeInitializeAtBuildTime (OriginalClassProvider .getJavaClass (type ));
176232 }
@@ -350,8 +406,26 @@ public void forceInitializeHosted(Class<?> clazz, String reason, boolean allowIn
350406 if (clazz == null ) {
351407 return ;
352408 }
409+
353410 classInitializationConfiguration .insert (clazz .getTypeName (), InitKind .BUILD_TIME , reason , true );
354411 InitKind initKind = ensureClassInitialized (clazz , allowInitializationErrors );
412+ if (initKind == InitKind .RUN_TIME ) {
413+ assert allowInitializationErrors || !LinkAtBuildTimeSupport .singleton ().linkAtBuildTime (clazz );
414+ if (ImageLayerBuildingSupport .buildingApplicationLayer ()) {
415+ /*
416+ * Special case for application layer building. If a base layer class was configured
417+ * with --initialize-at-build-time but its initialization failed, then the computed
418+ * init kind will be RUN_TIME, different from its configured init kind of
419+ * BUILD_TIME. In the app layer the computed init kind from the previous layer is
420+ * registered as the configured init kind, but if the --initialize-at-build-time was
421+ * already processed for the class then it will already have a conflicting
422+ * configured init kind of BUILD_TIME. Update the configuration registry to allow
423+ * the RUN_TIME registration coming from the base layer. (GR-65405)
424+ */
425+ classInitializationConfiguration .updateStrict (clazz .getTypeName (), InitKind .BUILD_TIME , InitKind .RUN_TIME ,
426+ "Allow the registration of the computed run time initialization kind from a previous layer for classes whose build time initialization fails." );
427+ }
428+ }
355429 classInitKinds .put (clazz , initKind );
356430
357431 forceInitializeHosted (clazz .getSuperclass (), "super type of " + clazz .getTypeName (), allowInitializationErrors );
@@ -369,7 +443,8 @@ private void forceInitializeInterfaces(Class<?>[] interfaces, String reason) {
369443 if (metaAccess .lookupJavaType (iface ).declaresDefaultMethods ()) {
370444 classInitializationConfiguration .insert (iface .getTypeName (), InitKind .BUILD_TIME , reason , true );
371445
372- ensureClassInitialized (iface , false );
446+ InitKind initKind = ensureClassInitialized (iface , false );
447+ VMError .guarantee (initKind == InitKind .BUILD_TIME , "Initialization of %s failed so all interfaces with default methods must be already initialized." , iface .getTypeName ());
373448 classInitKinds .put (iface , InitKind .BUILD_TIME );
374449 }
375450 forceInitializeInterfaces (iface .getInterfaces (), "super type of " + iface .getTypeName ());
0 commit comments