Skip to content

Commit 8b37cb6

Browse files
committed
When BUILD_TIME init fails in app layer update the configuration registry to reflect this.
1 parent e757db4 commit 8b37cb6

File tree

2 files changed

+91
-9
lines changed

2 files changed

+91
-9
lines changed

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationConfiguration.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.graalvm.collections.Pair;
3636

3737
import com.oracle.svm.core.util.UserError;
38+
import com.oracle.svm.core.util.VMError;
3839

3940
/**
4041
* Maintains user and system configuration for class initialization in Native Image.
@@ -52,12 +53,47 @@
5253
*
5354
* Every node tracks a list of reasons for the set configuration. This list helps the users debug
5455
* conflicts in the configuration.
56+
*
57+
* A {@code strict} configuration, as specified by {@link InitializationNode#strict}, defines
58+
* whether an initialization kind, as specified by {@link InitializationNode#kind}, was explicitly
59+
* configured for the corresponding type. A {@code non-strict} configuration is a configuration
60+
* inherited from a parent package configuration.
5561
*/
5662
final class ClassInitializationConfiguration {
5763
private static final String ROOT_QUALIFIER = "";
5864
private static final int MAX_NUMBER_OF_REASONS = 10;
5965

60-
private InitializationNode root = new InitializationNode("", null, null, false);
66+
private final InitializationNode root = new InitializationNode("", null, null, false);
67+
68+
synchronized void updateStrict(String classOrPackage, InitKind expectedKind, InitKind newKind, String reason) {
69+
updateStrictRec(root, qualifierList(classOrPackage), expectedKind, newKind, reason);
70+
}
71+
72+
private void updateStrictRec(InitializationNode node, List<String> classOrPackage, InitKind expectedKind, InitKind newKind, String reason) {
73+
assert !classOrPackage.isEmpty();
74+
assert node.qualifier.equals(classOrPackage.getFirst());
75+
if (classOrPackage.size() == 1) {
76+
VMError.guarantee(node.strict, "Expected a strict initialization policy for %s.", qualifiedName(node), newKind);
77+
VMError.guarantee(newKind != expectedKind, "The initialization policy for %s is already %s.", qualifiedName(node), newKind);
78+
VMError.guarantee(node.kind == expectedKind, "Found unexpected initialization policy for %s: expected %s, found %s.", qualifiedName(node), expectedKind, node.kind);
79+
updateReason(node, reason);
80+
node.kind = newKind;
81+
} else {
82+
List<String> tail = new ArrayList<>(classOrPackage);
83+
tail.removeFirst();
84+
String nextQualifier = tail.getFirst();
85+
VMError.guarantee(node.children.containsKey(nextQualifier), "Expected that initialization node for %s already contains a child for %s.", qualifiedName(node), nextQualifier);
86+
updateStrictRec(node.children.get(nextQualifier), tail, expectedKind, newKind, reason);
87+
}
88+
}
89+
90+
private static void updateReason(InitializationNode node, String reason) {
91+
if (node.reasons.size() < MAX_NUMBER_OF_REASONS) {
92+
node.reasons.add(reason);
93+
} else if (node.reasons.size() == MAX_NUMBER_OF_REASONS) {
94+
node.reasons.add("others");
95+
}
96+
}
6197

6298
public synchronized void insert(String classOrPackage, InitKind kind, String reason, boolean strict) {
6399
assert kind != null;
@@ -93,11 +129,7 @@ private void insertRec(InitializationNode node, List<String> classOrPackage, Ini
93129
node.strict = strict;
94130
node.reasons.add(reason);
95131
} else if (node.kind == kind) {
96-
if (node.reasons.size() < MAX_NUMBER_OF_REASONS) {
97-
node.reasons.add(reason);
98-
} else if (node.reasons.size() == MAX_NUMBER_OF_REASONS) {
99-
node.reasons.add("others");
100-
}
132+
updateReason(node, reason);
101133
} else {
102134
if (node.strict) {
103135
throw UserError.abort("Incompatible change of initialization policy for %s: trying to change %s %s to %s %s",

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,19 @@
4444
import java.util.function.Function;
4545
import java.util.stream.Collectors;
4646

47-
import jdk.graal.compiler.core.common.ContextClassLoaderScope;
48-
import org.graalvm.nativeimage.libgraal.hosted.LibGraalLoader;
4947
import org.graalvm.collections.EconomicSet;
5048
import org.graalvm.nativeimage.ImageSingletons;
5149
import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;
5250
import org.graalvm.nativeimage.impl.clinit.ClassInitializationTracking;
51+
import org.graalvm.nativeimage.libgraal.hosted.LibGraalLoader;
5352

53+
import com.oracle.graal.pointsto.BigBang;
5454
import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider;
5555
import com.oracle.graal.pointsto.meta.AnalysisType;
5656
import com.oracle.graal.pointsto.meta.BaseLayerType;
5757
import com.oracle.graal.pointsto.reports.ReportUtils;
5858
import com.oracle.svm.core.SubstrateOptions;
59+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
5960
import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue;
6061
import com.oracle.svm.core.option.SubstrateOptionsParser;
6162
import com.oracle.svm.core.util.UserError;
@@ -65,6 +66,7 @@
6566
import com.oracle.svm.util.LogUtils;
6667
import com.oracle.svm.util.ModuleSupport;
6768

69+
import jdk.graal.compiler.core.common.ContextClassLoaderScope;
6870
import jdk.graal.compiler.java.LambdaUtils;
6971
import jdk.internal.misc.Unsafe;
7072
import jdk.vm.ci.meta.MetaAccessProvider;
@@ -73,6 +75,38 @@
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
*/
77111
public class ClassInitializationSupport implements RuntimeClassInitializationSupport {
78112

@@ -372,8 +406,23 @@ public void forceInitializeHosted(Class<?> clazz, String reason, boolean allowIn
372406
if (clazz == null) {
373407
return;
374408
}
409+
375410
classInitializationConfiguration.insert(clazz.getTypeName(), InitKind.BUILD_TIME, reason, true);
376411
InitKind initKind = ensureClassInitialized(clazz, allowInitializationErrors);
412+
if (ImageLayerBuildingSupport.buildingApplicationLayer() && initKind == InitKind.RUN_TIME) {
413+
/*
414+
* Special case for application layer building. If a base layer class was configured
415+
* with --initialize-at-build-time but its initialization failed, then the computed init
416+
* kind will be RUN_TIME, different from its configured init kind of BUILD_TIME. In the
417+
* app layer the computed init kind from the previous layer is registered as the
418+
* configured init kind, but if the --initialize-at-build-time was already processed for
419+
* the class then it will already have a conflicting configured init kind of BUILD_TIME.
420+
* Update the configuration registry to allow the RUN_TIME registration coming from the
421+
* base layer. (GR-65405)
422+
*/
423+
assert allowInitializationErrors || !LinkAtBuildTimeSupport.singleton().linkAtBuildTime(clazz);
424+
classInitializationConfiguration.updateStrict(clazz.getTypeName(), InitKind.BUILD_TIME, InitKind.RUN_TIME, "Build time initialization failed.");
425+
}
377426
classInitKinds.put(clazz, initKind);
378427

379428
forceInitializeHosted(clazz.getSuperclass(), "super type of " + clazz.getTypeName(), allowInitializationErrors);
@@ -391,7 +440,8 @@ private void forceInitializeInterfaces(Class<?>[] interfaces, String reason) {
391440
if (metaAccess.lookupJavaType(iface).declaresDefaultMethods()) {
392441
classInitializationConfiguration.insert(iface.getTypeName(), InitKind.BUILD_TIME, reason, true);
393442

394-
ensureClassInitialized(iface, false);
443+
InitKind initKind = ensureClassInitialized(iface, false);
444+
VMError.guarantee(initKind == InitKind.BUILD_TIME, "Initialization of %s failed so all interfaces with default methods must be already initialized.", iface.getTypeName());
395445
classInitKinds.put(iface, InitKind.BUILD_TIME);
396446
}
397447
forceInitializeInterfaces(iface.getInterfaces(), "super type of " + iface.getTypeName());

0 commit comments

Comments
 (0)