Skip to content

Commit 64c4a87

Browse files
committed
[GR-65925] Allow classes with failed initialization to be registered again in the app layer.
PullRequest: graal/21108
2 parents 180489e + 90f16e6 commit 64c4a87

File tree

6 files changed

+104
-85
lines changed

6 files changed

+104
-85
lines changed

substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,33 +27,35 @@ struct PersistedAnalysisType {
2727
isEnum @8 :Bool;
2828
# True if the type's initialization status was computed as BUILD_TIME. Build-time initialized types are not simulated.
2929
isInitialized @9 :Bool;
30-
isLinked @10 :Bool;
31-
sourceFileName @11 :Text;
32-
enclosingTypeId @12 :TypeId;
33-
componentTypeId @13 :TypeId;
34-
superClassTypeId @14 :TypeId;
35-
isInstantiated @15 :Bool;
36-
isUnsafeAllocated @16 :Bool;
37-
isReachable @17 :Bool;
38-
interfaces @18 :List(TypeId);
39-
instanceFieldIds @19 :List(FieldId);
40-
instanceFieldIdsWithSuper @20 :List(FieldId);
41-
staticFieldIds @21 :List(FieldId);
42-
annotationList @22 :List(Annotation);
43-
classInitializationInfo @23 :ClassInitializationInfo;
44-
hasArrayType @24 :Bool;
45-
subTypes @25 :List(TypeId);
46-
isAnySubtypeInstantiated @26 :Bool;
30+
# True if the type was configured as initialized at BUILD_TIME but initialization failed so it was registered as RUN_TIME.
31+
isFailedInitialization @10 :Bool;
32+
isLinked @11 :Bool;
33+
sourceFileName @12 :Text;
34+
enclosingTypeId @13 :TypeId;
35+
componentTypeId @14 :TypeId;
36+
superClassTypeId @15 :TypeId;
37+
isInstantiated @16 :Bool;
38+
isUnsafeAllocated @17 :Bool;
39+
isReachable @18 :Bool;
40+
interfaces @19 :List(TypeId);
41+
instanceFieldIds @20 :List(FieldId);
42+
instanceFieldIdsWithSuper @21 :List(FieldId);
43+
staticFieldIds @22 :List(FieldId);
44+
annotationList @23 :List(Annotation);
45+
classInitializationInfo @24 :ClassInitializationInfo;
46+
hasArrayType @25 :Bool;
47+
subTypes @26 :List(TypeId);
48+
isAnySubtypeInstantiated @27 :Bool;
4749
wrappedType :union {
48-
none @27 :Void; # default
50+
none @28 :Void; # default
4951
serializationGenerated :group {
50-
rawDeclaringClass @28 :Text;
51-
rawTargetConstructor @29 :Text;
52+
rawDeclaringClass @29 :Text;
53+
rawTargetConstructor @30 :Text;
5254
}
5355
lambda :group {
54-
capturingClass @30 :Text;
56+
capturingClass @31 :Text;
5557
}
56-
proxyType @31 :Void;
58+
proxyType @32 :Void;
5759
}
5860
}
5961

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

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

3737
import com.oracle.svm.core.util.UserError;
38-
import com.oracle.svm.core.util.VMError;
3938

4039
/**
4140
* Maintains user and system configuration for class initialization in Native Image.
@@ -65,36 +64,6 @@ final class ClassInitializationConfiguration {
6564

6665
private final InitializationNode root = new InitializationNode("", null, null, false);
6766

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-
}
97-
9867
public synchronized void insert(String classOrPackage, InitKind kind, String reason, boolean strict) {
9968
assert kind != null;
10069
insertRec(root, qualifierList(classOrPackage), kind, reason, strict);
@@ -129,7 +98,11 @@ private void insertRec(InitializationNode node, List<String> classOrPackage, Ini
12998
node.strict = strict;
13099
node.reasons.add(reason);
131100
} else if (node.kind == kind) {
132-
updateReason(node, reason);
101+
if (node.reasons.size() < MAX_NUMBER_OF_REASONS) {
102+
node.reasons.add(reason);
103+
} else if (node.reasons.size() == MAX_NUMBER_OF_REASONS) {
104+
node.reasons.add("others");
105+
}
133106
} else {
134107
if (node.strict) {
135108
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: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ public class ClassInitializationSupport implements RuntimeClassInitializationSup
123123
*/
124124
final ConcurrentMap<Class<?>, InitKind> classInitKinds = new ConcurrentHashMap<>();
125125

126+
/**
127+
* Store classes that were configured with --initialize-at-build-time but for which
128+
* initialization failed and were registered as initialized at run time either because
129+
* initialization errors during registration were allowed or they were not configured with
130+
* --link-at-build-time.
131+
*/
132+
final Set<Class<?>> requestedAtBuildTimeButFailedInit = ConcurrentHashMap.newKeySet();
133+
126134
/**
127135
* We need always-reached types to avoid users injecting class initialization checks in our VM
128136
* implementation and hot paths and to prevent users from making the whole class hierarchy
@@ -196,12 +204,26 @@ private void setConfigurationSealed(boolean sealed) {
196204
}
197205

198206
/**
199-
* Returns an init kind for {@code clazz}.
207+
* Returns the configured init kind for {@code clazz}.
200208
*/
201209
InitKind specifiedInitKindFor(Class<?> clazz) {
202210
return classInitializationConfiguration.lookupKind(clazz.getTypeName()).getLeft();
203211
}
204212

213+
/**
214+
* Returns the computed init kind for {@code clazz}, which can differ from the configured init
215+
* kind returned by {@link #specifiedInitKindFor(Class)}.
216+
*/
217+
InitKind computedInitKindFor(Class<?> clazz) {
218+
return classInitKinds.get(clazz);
219+
}
220+
221+
public boolean isFailedInitialization(Class<?> clazz) {
222+
boolean failedInit = requestedAtBuildTimeButFailedInit.contains(clazz);
223+
VMError.guarantee(!failedInit || specifiedInitKindFor(clazz) == InitKind.BUILD_TIME && computedInitKindFor(clazz) == InitKind.RUN_TIME);
224+
return failedInit;
225+
}
226+
205227
Boolean isStrictlyDefined(Class<?> clazz) {
206228
return classInitializationConfiguration.lookupKind(clazz.getTypeName()).getRight();
207229
}
@@ -410,19 +432,15 @@ public void forceInitializeHosted(Class<?> clazz, String reason, boolean allowIn
410432
InitKind initKind = ensureClassInitialized(clazz, allowInitializationErrors);
411433
if (initKind == InitKind.RUN_TIME) {
412434
assert allowInitializationErrors || !LinkAtBuildTimeSupport.singleton().linkAtBuildTime(clazz);
413-
if (ImageLayerBuildingSupport.buildingExtensionLayer()) {
435+
if (ImageLayerBuildingSupport.buildingImageLayer()) {
414436
/*
415-
* Special case for application layer building. If a base layer class was configured
416-
* with --initialize-at-build-time but its initialization failed, then the computed
417-
* init kind will be RUN_TIME, different from its configured init kind of
418-
* BUILD_TIME. In the app layer the computed init kind from the previous layer is
419-
* registered as the configured init kind, but if the --initialize-at-build-time was
420-
* already processed for the class then it will already have a conflicting
421-
* configured init kind of BUILD_TIME. Update the configuration registry to allow
422-
* the RUN_TIME registration coming from the base layer. (GR-65405)
437+
* Record class configured with --initialize-at-build-time but for which
438+
* initialization failed so it's registered as initialized at run time. To ensure
439+
* that the state of class initialization registries is consistent between layers
440+
* we'll attempt to init it again in the next layer and verify that it fails. Class
441+
* initialization in layered images will be further refined by (GR-65405).
423442
*/
424-
classInitializationConfiguration.updateStrict(clazz.getTypeName(), InitKind.BUILD_TIME, InitKind.RUN_TIME,
425-
"Allow the registration of the computed run time initialization kind from a previous layer for classes whose build time initialization fails.");
443+
requestedAtBuildTimeButFailedInit.add(clazz);
426444
}
427445
}
428446
classInitKinds.put(clazz, initKind);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,19 @@ private void initializeBaseLayerTypeBeforePublishing(AnalysisType type, Persiste
681681
if (typeData.getIsInitialized()) {
682682
classInitializationSupport.withUnsealedConfiguration(() -> classInitializationSupport.initializeAtBuildTime(clazz, "computed in a previous layer"));
683683
} else {
684-
classInitializationSupport.withUnsealedConfiguration(() -> classInitializationSupport.initializeAtRunTime(clazz, "computed in a previous layer"));
684+
if (typeData.getIsFailedInitialization()) {
685+
/*
686+
* In the previous layer this class was configured with --initialize-at-build-time
687+
* but its initialization failed so it was registered as run time initialized. We
688+
* attempt to init it again in this layer and verify that it fails. This will allow
689+
* the class to be configured again in this layer with --initialize-at-build-time,
690+
* either before or after this step.
691+
*/
692+
classInitializationSupport.withUnsealedConfiguration(() -> classInitializationSupport.initializeAtBuildTime(clazz, "computed in a previous layer"));
693+
VMError.guarantee(classInitializationSupport.isFailedInitialization(clazz), "Expected the initialization to fail for %s, as it has failed in a previous layer.", clazz);
694+
} else {
695+
classInitializationSupport.withUnsealedConfiguration(() -> classInitializationSupport.initializeAtRunTime(clazz, "computed in a previous layer"));
696+
}
685697
}
686698

687699
/* Extract and record the base layer identity hashcode for this type. */

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ public class SVMImageLayerWriter extends ImageLayerWriter {
201201

202202
private NativeImageHeap nativeImageHeap;
203203
private HostedUniverse hUniverse;
204+
private final ClassInitializationSupport classInitializationSupport;
204205

205206
private boolean polymorphicSignatureSealed = false;
206207

@@ -268,6 +269,7 @@ public SVMImageLayerWriter(SVMImageLayerSnapshotUtil imageLayerSnapshotUtil, boo
268269
this.useSharedLayerGraphs = useSharedLayerGraphs;
269270
this.useSharedLayerStrengthenedGraphs = useSharedLayerStrengthenedGraphs;
270271
graphsOutput = new GraphsOutput();
272+
this.classInitializationSupport = ClassInitializationSupport.singleton();
271273
}
272274

273275
public void setInternedStringsIdentityMap(IdentityHashMap<String, String> map) {
@@ -447,6 +449,7 @@ private void persistType(AnalysisType type, Supplier<PersistedAnalysisType.Build
447449
builder.setIsInterface(type.isInterface());
448450
builder.setIsEnum(type.isEnum());
449451
builder.setIsInitialized(type.isInitialized());
452+
builder.setIsFailedInitialization(classInitializationSupport.isFailedInitialization(type.getJavaClass()));
450453
builder.setIsLinked(type.isLinked());
451454
if (type.getSourceFileName() != null) {
452455
builder.setSourceFileName(type.getSourceFileName());

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,20 @@ public final void setIsInitialized(boolean value) {
168168
_setBooleanField(98, value);
169169
}
170170

171-
public final boolean getIsLinked() {
171+
public final boolean getIsFailedInitialization() {
172172
return _getBooleanField(99);
173173
}
174-
public final void setIsLinked(boolean value) {
174+
public final void setIsFailedInitialization(boolean value) {
175175
_setBooleanField(99, value);
176176
}
177177

178+
public final boolean getIsLinked() {
179+
return _getBooleanField(100);
180+
}
181+
public final void setIsLinked(boolean value) {
182+
_setBooleanField(100, value);
183+
}
184+
178185
public final boolean hasSourceFileName() {
179186
return !_pointerFieldIsNull(4);
180187
}
@@ -212,24 +219,24 @@ public final void setSuperClassTypeId(int value) {
212219
}
213220

214221
public final boolean getIsInstantiated() {
215-
return _getBooleanField(100);
222+
return _getBooleanField(101);
216223
}
217224
public final void setIsInstantiated(boolean value) {
218-
_setBooleanField(100, value);
225+
_setBooleanField(101, value);
219226
}
220227

221228
public final boolean getIsUnsafeAllocated() {
222-
return _getBooleanField(101);
229+
return _getBooleanField(102);
223230
}
224231
public final void setIsUnsafeAllocated(boolean value) {
225-
_setBooleanField(101, value);
232+
_setBooleanField(102, value);
226233
}
227234

228235
public final boolean getIsReachable() {
229-
return _getBooleanField(102);
236+
return _getBooleanField(103);
230237
}
231238
public final void setIsReachable(boolean value) {
232-
_setBooleanField(102, value);
239+
_setBooleanField(103, value);
233240
}
234241

235242
public final boolean hasInterfaces() {
@@ -302,10 +309,10 @@ public final com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchema
302309
return _initPointerField(com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.ClassInitializationInfo.factory,10, 0);
303310
}
304311
public final boolean getHasArrayType() {
305-
return _getBooleanField(103);
312+
return _getBooleanField(104);
306313
}
307314
public final void setHasArrayType(boolean value) {
308-
_setBooleanField(103, value);
315+
_setBooleanField(104, value);
309316
}
310317

311318
public final boolean hasSubTypes() {
@@ -321,10 +328,10 @@ public final com.oracle.svm.shaded.org.capnproto.PrimitiveList.Int.Builder initS
321328
return _initPointerField(com.oracle.svm.shaded.org.capnproto.PrimitiveList.Int.factory, 11, size);
322329
}
323330
public final boolean getIsAnySubtypeInstantiated() {
324-
return _getBooleanField(104);
331+
return _getBooleanField(105);
325332
}
326333
public final void setIsAnySubtypeInstantiated(boolean value) {
327-
_setBooleanField(104, value);
334+
_setBooleanField(105, value);
328335
}
329336

330337
public final WrappedType.Builder getWrappedType() {
@@ -396,10 +403,14 @@ public final boolean getIsInitialized() {
396403
return _getBooleanField(98);
397404
}
398405

399-
public final boolean getIsLinked() {
406+
public final boolean getIsFailedInitialization() {
400407
return _getBooleanField(99);
401408
}
402409

410+
public final boolean getIsLinked() {
411+
return _getBooleanField(100);
412+
}
413+
403414
public boolean hasSourceFileName() {
404415
return !_pointerFieldIsNull(4);
405416
}
@@ -420,15 +431,15 @@ public final int getSuperClassTypeId() {
420431
}
421432

422433
public final boolean getIsInstantiated() {
423-
return _getBooleanField(100);
434+
return _getBooleanField(101);
424435
}
425436

426437
public final boolean getIsUnsafeAllocated() {
427-
return _getBooleanField(101);
438+
return _getBooleanField(102);
428439
}
429440

430441
public final boolean getIsReachable() {
431-
return _getBooleanField(102);
442+
return _getBooleanField(103);
432443
}
433444

434445
public final boolean hasInterfaces() {
@@ -474,7 +485,7 @@ public com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder
474485
}
475486

476487
public final boolean getHasArrayType() {
477-
return _getBooleanField(103);
488+
return _getBooleanField(104);
478489
}
479490

480491
public final boolean hasSubTypes() {
@@ -485,7 +496,7 @@ public final com.oracle.svm.shaded.org.capnproto.PrimitiveList.Int.Reader getSub
485496
}
486497

487498
public final boolean getIsAnySubtypeInstantiated() {
488-
return _getBooleanField(104);
499+
return _getBooleanField(105);
489500
}
490501

491502
public WrappedType.Reader getWrappedType() {

0 commit comments

Comments
 (0)