Skip to content

Commit aad8fcc

Browse files
committed
All registration after analysis throw UserError.
Refactorization of sealed across implementations of ConditionalConfigurationRegistry.
1 parent f6b10d6 commit aad8fcc

File tree

7 files changed

+66
-57
lines changed

7 files changed

+66
-57
lines changed

substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsFeature.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ public class ForeignFunctionsFeature implements InternalFeature {
155155
"jdk.internal.foreign.layout"});
156156

157157
/** Indicates if the registration of stubs is no longer allowed. */
158-
private boolean sealed;
159158
private RuntimeForeignAccessSupportImpl accessSupport;
160159

161160
/** Indicates if at least one stub was registered. */
@@ -173,10 +172,6 @@ public static ForeignFunctionsFeature singleton() {
173172
return ImageSingletons.lookup(ForeignFunctionsFeature.class);
174173
}
175174

176-
private void checkNotSealed() {
177-
UserError.guarantee(!sealed, "Registration of foreign functions was closed.");
178-
}
179-
180175
/**
181176
* Descriptor that represents both, up- and downcalls.
182177
*/
@@ -219,7 +214,7 @@ void duringSetup(AnalysisMetaAccess metaAccess, AnalysisUniverse analysisUnivers
219214

220215
@Override
221216
public void registerForDowncall(ConfigurationCondition condition, FunctionDescriptor desc, Linker.Option... options) {
222-
checkNotSealed();
217+
abortIfSealed();
223218
try {
224219
LinkerOptions linkerOptions = LinkerOptions.forDowncall(desc, options);
225220
SharedDesc sharedDesc = new SharedDesc(desc, linkerOptions);
@@ -231,7 +226,7 @@ public void registerForDowncall(ConfigurationCondition condition, FunctionDescri
231226

232227
@Override
233228
public void registerForUpcall(ConfigurationCondition condition, FunctionDescriptor desc, Linker.Option... options) {
234-
checkNotSealed();
229+
abortIfSealed();
235230
try {
236231
LinkerOptions linkerOptions = LinkerOptions.forUpcall(desc, options);
237232
SharedDesc sharedDesc = new SharedDesc(desc, linkerOptions);
@@ -243,7 +238,7 @@ public void registerForUpcall(ConfigurationCondition condition, FunctionDescript
243238

244239
@Override
245240
public void registerForDirectUpcall(ConfigurationCondition condition, MethodHandle target, FunctionDescriptor desc, Linker.Option... options) {
246-
checkNotSealed();
241+
abortIfSealed();
247242
DirectMethodHandleDesc directMethodHandleDesc = target.describeConstable()
248243
.filter(x -> x instanceof DirectMethodHandleDesc dmh && dmh.kind() == Kind.STATIC)
249244
.map(x -> ((DirectMethodHandleDesc) x))
@@ -706,7 +701,7 @@ public void beforeAnalysis(BeforeAnalysisAccess a) {
706701

707702
@Override
708703
public void afterAnalysis(AfterAnalysisAccess access) {
709-
sealed = true;
704+
accessSupport.sealed();
710705
if (!ForeignFunctionsRuntime.areFunctionCallsSupported() && stubsRegistered) {
711706
assert getCreatedDowncallStubsCount() == 0;
712707
assert getCreatedUpcallStubsCount() == 0;
@@ -840,17 +835,17 @@ public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, Va
840835
/* Testing and reporting interface */
841836

842837
public int getCreatedDowncallStubsCount() {
843-
assert sealed;
838+
assert accessSupport.isSealed();
844839
return foreignFunctionsRuntime.getDowncallStubsCount();
845840
}
846841

847842
public int getCreatedUpcallStubsCount() {
848-
assert sealed;
843+
assert accessSupport.isSealed();
849844
return foreignFunctionsRuntime.getUpcallStubsCount();
850845
}
851846

852847
public int getCreatedDirectUpcallStubsCount() {
853-
assert sealed;
848+
assert accessSupport.isSealed();
854849
return foreignFunctionsRuntime.getDirectUpcallStubsCount();
855850
}
856851
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.concurrent.ConcurrentLinkedQueue;
3333
import java.util.function.Consumer;
3434

35+
import com.oracle.svm.core.util.UserError;
3536
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
3637
import org.graalvm.nativeimage.hosted.Feature;
3738
import org.graalvm.nativeimage.impl.ConfigurationCondition;
@@ -42,6 +43,7 @@
4243
public abstract class ConditionalConfigurationRegistry {
4344
private Feature.BeforeAnalysisAccess beforeAnalysisAccess;
4445
private SVMHost hostVM;
46+
private boolean sealed = false;
4547
protected AnalysisUniverse universe;
4648
private final Map<Class<?>, Collection<Runnable>> pendingReachabilityHandlers = new ConcurrentHashMap<>();
4749
private final Set<ConditionalTask> pendingConditionalTasks = ConcurrentHashMap.newKeySet();
@@ -117,6 +119,24 @@ protected String nullErrorMessage(String elementKind, String accessKind) {
117119
return "Cannot register null value as " + elementKind + " for " + accessKind + ". Please ensure that all values you register are not null.";
118120
}
119121

122+
public void sealed() {
123+
sealed = true;
124+
}
125+
126+
public boolean isSealed() {
127+
return sealed;
128+
}
129+
130+
public void abortIfSealed() {
131+
/*
132+
* The UserError needs a cause argument to print out the stack trace, so users can see
133+
* exactly where the exception occurred.
134+
*/
135+
if (sealed) {
136+
throw UserError.abort(new IllegalStateException(), "All elements must be registered for runtime access before the analysis has completed.");
137+
}
138+
}
139+
120140
public void setHostVM(SVMHost hostVM) {
121141
this.hostVM = hostVM;
122142
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public static class Options {
160160
public static final HostedOptionKey<Boolean> GenerateEmbeddedResourcesFile = new HostedOptionKey<>(false);
161161
}
162162

163-
private boolean sealed = false;
163+
private ResourcesRegistryImpl resourcesRegistry;
164164

165165
private record ConditionalPattern(ConfigurationCondition condition, String pattern, Object origin) {
166166
}
@@ -229,31 +229,34 @@ public void addResourceEntry(Module module, String resourcePath, Object origin)
229229

230230
@Override
231231
public void injectResource(Module module, String resourcePath, byte[] resourceContent, Object origin) {
232+
abortIfSealed();
232233
EmbeddedResourcesInfo.singleton().declareResourceAsRegistered(module, resourcePath, "INJECTED", origin);
233234
Resources.currentLayer().registerResource(module, resourcePath, resourceContent);
234235
}
235236

236237
@Override
237238
public void ignoreResources(ConfigurationCondition condition, String pattern, Object origin) {
239+
abortIfSealed();
238240
registerConditionalConfiguration(condition, (cnd) -> {
239-
UserError.guarantee(!sealed, "Resources ignored too late: %s", pattern);
240-
241241
excludedResourcePatterns.add(new ConditionalPattern(condition, pattern, origin));
242242
});
243243
}
244244

245245
@Override
246246
public void addResourceBundles(ConfigurationCondition condition, String name) {
247+
abortIfSealed();
247248
registerConditionalConfiguration(condition, (cnd) -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(cnd, name));
248249
}
249250

250251
@Override
251252
public void addClassBasedResourceBundle(ConfigurationCondition condition, String basename, String className) {
253+
abortIfSealed();
252254
registerConditionalConfiguration(condition, (cnd) -> ImageSingletons.lookup(LocalizationFeature.class).prepareClassResourceBundle(basename, className));
253255
}
254256

255257
@Override
256258
public void addResourceBundles(ConfigurationCondition condition, String basename, Collection<Locale> locales) {
259+
abortIfSealed();
257260
registerConditionalConfiguration(condition, (cnd) -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(cnd, basename, locales));
258261
}
259262

@@ -388,7 +391,7 @@ private void registerResource(Module module, String resourcePath, boolean fromJa
388391
public void afterRegistration(AfterRegistrationAccess a) {
389392
FeatureImpl.AfterRegistrationAccessImpl access = (FeatureImpl.AfterRegistrationAccessImpl) a;
390393
imageClassLoader = access.getImageClassLoader();
391-
ResourcesRegistryImpl resourcesRegistry = new ResourcesRegistryImpl();
394+
resourcesRegistry = new ResourcesRegistryImpl();
392395
ImageSingletons.add(ResourcesRegistry.class, resourcesRegistry);
393396
ImageSingletons.add(RuntimeResourceSupport.class, resourcesRegistry);
394397
EmbeddedResourcesInfo embeddedResourcesInfo = new EmbeddedResourcesInfo();
@@ -649,7 +652,7 @@ boolean moduleNameMatches(String resourceContainerModuleName) {
649652

650653
@Override
651654
public void afterAnalysis(AfterAnalysisAccess access) {
652-
sealed = true;
655+
resourcesRegistry.sealed();
653656
if (Options.GenerateEmbeddedResourcesFile.getValue()) {
654657
Path reportLocation = NativeImageGenerator.generatedFiles(HostedOptionValues.singleton()).resolve(Options.EMBEDDED_RESOURCES_FILE_NAME);
655658
try (JsonWriter writer = new JsonWriter(reportLocation)) {
@@ -709,7 +712,7 @@ public boolean isDecorator() {
709712

710713
@Override
711714
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
712-
VMError.guarantee(!sealed, "All bytecode parsing happens before the analysis, i.e., before the registry is sealed");
715+
VMError.guarantee(!resourcesRegistry.isSealed(), "All bytecode parsing happens before the analysis, i.e., before the registry is sealed");
713716
Class<?> clazz = SubstrateGraphBuilderPlugins.asConstantObject(b, Class.class, receiver.get(false));
714717
String resource = SubstrateGraphBuilderPlugins.asConstantObject(b, String.class, arg);
715718
if (clazz != null && resource != null) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,8 @@ static final class JNICallableJavaMethod {
165165
}
166166
}
167167

168-
private JNIRuntimeAccessibilitySupportImpl runtimeSupport;
168+
private JNIRuntimeAccessibilitySupportImpl runtimeSupport = new JNIRuntimeAccessibilitySupportImpl();
169169

170-
private boolean sealed = false;
171170
private final Map<String, JNICallTrampolineMethod> trampolineMethods = new ConcurrentHashMap<>();
172171
private final Map<ResolvedSignature<ResolvedJavaType>, JNIJavaCallWrapperMethod> javaCallWrapperMethods = new ConcurrentHashMap<>();
173172
private final Map<ResolvedSignature<ResolvedJavaType>, JNIJavaCallVariantWrapperGroup> callVariantWrappers = new ConcurrentHashMap<>();
@@ -191,10 +190,6 @@ public static class Options {
191190
public static final HostedOptionKey<Boolean> PrintJNIMethods = new HostedOptionKey<>(false);
192191
}
193192

194-
private void abortIfSealed() {
195-
UserError.guarantee(!sealed, "Classes, methods and fields must be registered for JNI access before the analysis has completed.");
196-
}
197-
198193
@Override
199194
public List<Class<? extends Feature>> getRequiredFeatures() {
200195
// Ensure that KnownOffsets is fully initialized before we access it
@@ -207,7 +202,6 @@ public void afterRegistration(AfterRegistrationAccess arg) {
207202

208203
JNIReflectionDictionary.create();
209204

210-
runtimeSupport = new JNIRuntimeAccessibilitySupportImpl();
211205
ImageSingletons.add(RuntimeJNIAccessSupport.class, runtimeSupport);
212206

213207
ConfigurationConditionResolver<ConfigurationCondition> conditionResolver = new NativeImageConditionResolver(access.getImageClassLoader(),
@@ -347,7 +341,7 @@ public JNICallTrampolineMethod getOrCreateCallTrampolineMethod(MetaAccessProvide
347341
}
348342

349343
public JNINativeLinkage makeLinkage(String declaringClass, String name, String descriptor) {
350-
UserError.guarantee(!sealed,
344+
UserError.guarantee(!runtimeSupport.isSealed(),
351345
"All linkages for JNI calls must be created before the analysis has completed.%nOffending class: %s name: %s descriptor: %s",
352346
declaringClass, name, descriptor);
353347

@@ -539,9 +533,9 @@ private static void addNegativeFieldLookup(Class<?> declaringClass, String field
539533
@Override
540534
@SuppressWarnings("unused")
541535
public void afterAnalysis(AfterAnalysisAccess access) {
542-
sealed = true;
536+
runtimeSupport.sealed();
543537
if (wereElementsAdded()) {
544-
abortIfSealed();
538+
runtimeSupport.abortIfSealed();
545539
}
546540

547541
int numClasses = 0;

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,6 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl
124124
private final ClassForNameSupport classForNameSupport;
125125
private LayeredReflectionDataBuilder layeredReflectionDataBuilder;
126126

127-
private boolean sealed;
128-
129127
// Reflection data
130128
private final Map<Class<?>, RecordComponent[]> registeredRecordComponents = new ConcurrentHashMap<>();
131129

@@ -186,9 +184,7 @@ public void beforeAnalysis(BeforeAnalysisAccessImpl beforeAnalysisAccess) {
186184
}
187185

188186
private void runConditionalInAnalysisTask(ConfigurationCondition condition, Consumer<ConfigurationCondition> task) {
189-
if (sealed) {
190-
throw new UnsupportedFeatureException("Too late to add classes, methods, and fields for reflective access. Registration must happen in a Feature before the analysis has finished.");
191-
}
187+
abortIfSealed();
192188
runConditionalTask(condition, task);
193189
}
194190

@@ -1174,7 +1170,7 @@ private static void reportLinkingErrors(Class<?> clazz, List<Throwable> errors)
11741170
}
11751171

11761172
protected void afterAnalysis() {
1177-
sealed = true;
1173+
sealed();
11781174
processedTypes = null;
11791175
if (!throwMissingRegistrationErrors()) {
11801176
pendingRecordClasses = null;
@@ -1184,7 +1180,7 @@ protected void afterAnalysis() {
11841180

11851181
@Override
11861182
public Map<Class<?>, Set<Class<?>>> getReflectionInnerClasses() {
1187-
assert sealed;
1183+
assert isSealed();
11881184
return Collections.unmodifiableMap(innerClasses);
11891185
}
11901186

@@ -1203,37 +1199,37 @@ public int getEnabledReflectionQueries(Class<?> clazz) {
12031199

12041200
@Override
12051201
public Map<AnalysisType, Map<AnalysisField, ConditionalRuntimeValue<Field>>> getReflectionFields() {
1206-
assert sealed;
1202+
assert isSealed();
12071203
return Collections.unmodifiableMap(registeredFields);
12081204
}
12091205

12101206
@Override
12111207
public Map<AnalysisType, Map<AnalysisMethod, ConditionalRuntimeValue<Executable>>> getReflectionExecutables() {
1212-
assert sealed;
1208+
assert isSealed();
12131209
return Collections.unmodifiableMap(registeredMethods);
12141210
}
12151211

12161212
@Override
12171213
public Object getAccessor(AnalysisMethod method) {
1218-
assert sealed;
1214+
assert isSealed();
12191215
return methodAccessors.get(method);
12201216
}
12211217

12221218
@Override
12231219
public Set<ResolvedJavaField> getHidingReflectionFields() {
1224-
assert sealed;
1220+
assert isSealed();
12251221
return Collections.unmodifiableSet(hidingFields);
12261222
}
12271223

12281224
@Override
12291225
public Set<ResolvedJavaMethod> getHidingReflectionMethods() {
1230-
assert sealed;
1226+
assert isSealed();
12311227
return Collections.unmodifiableSet(hidingMethods);
12321228
}
12331229

12341230
@Override
12351231
public RecordComponent[] getRecordComponents(Class<?> type) {
1236-
assert sealed;
1232+
assert isSealed();
12371233
return registeredRecordComponents.get(type);
12381234
}
12391235

@@ -1242,7 +1238,7 @@ public void registerHeapDynamicHub(Object object, ScanReason reason) {
12421238
DynamicHub hub = (DynamicHub) object;
12431239
Class<?> javaClass = hub.getHostedJavaClass();
12441240
if (heapDynamicHubs.add(hub)) {
1245-
if (sealed) {
1241+
if (isSealed()) {
12461242
throw new UnsupportedFeatureException("Registering new class for reflection when the image heap is already sealed: " + javaClass);
12471243
}
12481244
if (!SubstitutionReflectivityFilter.shouldExclude(javaClass, metaAccess, universe)) {
@@ -1253,15 +1249,15 @@ public void registerHeapDynamicHub(Object object, ScanReason reason) {
12531249

12541250
@Override
12551251
public Set<DynamicHub> getHeapDynamicHubs() {
1256-
assert sealed;
1252+
assert isSealed();
12571253
return Collections.unmodifiableSet(heapDynamicHubs);
12581254
}
12591255

12601256
@Override
12611257
public void registerHeapReflectionField(Field reflectField, ScanReason reason) {
12621258
AnalysisField analysisField = metaAccess.lookupJavaField(reflectField);
12631259
if (heapFields.put(analysisField, reflectField) == null) {
1264-
if (sealed) {
1260+
if (isSealed()) {
12651261
throw new UnsupportedFeatureException("Registering new field for reflection when the image heap is already sealed: " + reflectField);
12661262
}
12671263
if (!SubstitutionReflectivityFilter.shouldExclude(reflectField, metaAccess, universe)) {
@@ -1286,7 +1282,7 @@ public void registerHeapReflectionExecutable(Executable reflectExecutable, ScanR
12861282
}
12871283
AnalysisMethod analysisMethod = metaAccess.lookupJavaMethod(reflectExecutable);
12881284
if (heapMethods.put(analysisMethod, reflectExecutable) == null) {
1289-
if (sealed) {
1285+
if (isSealed()) {
12901286
throw new UnsupportedFeatureException("Registering new method for reflection when the image heap is already sealed: " + reflectExecutable);
12911287
}
12921288
if (!SubstitutionReflectivityFilter.shouldExclude(reflectExecutable, metaAccess, universe)) {
@@ -1300,13 +1296,13 @@ public void registerHeapReflectionExecutable(Executable reflectExecutable, ScanR
13001296

13011297
@Override
13021298
public Map<AnalysisField, Field> getHeapReflectionFields() {
1303-
assert sealed;
1299+
assert isSealed();
13041300
return Collections.unmodifiableMap(heapFields);
13051301
}
13061302

13071303
@Override
13081304
public Map<AnalysisMethod, Executable> getHeapReflectionExecutables() {
1309-
assert sealed;
1305+
assert isSealed();
13101306
return Collections.unmodifiableMap(heapMethods);
13111307
}
13121308

@@ -1353,21 +1349,21 @@ public Map<Class<?>, Throwable> getRecordComponentLookupErrors() {
13531349
private static final AnnotationValue[] NO_ANNOTATIONS = new AnnotationValue[0];
13541350

13551351
public AnnotationValue[] getAnnotationData(AnnotatedElement element) {
1356-
assert sealed;
1352+
assert isSealed();
13571353
return filteredAnnotations.getOrDefault(element, NO_ANNOTATIONS);
13581354
}
13591355

13601356
private static final AnnotationValue[][] NO_PARAMETER_ANNOTATIONS = new AnnotationValue[0][0];
13611357

13621358
public AnnotationValue[][] getParameterAnnotationData(AnalysisMethod element) {
1363-
assert sealed;
1359+
assert isSealed();
13641360
return filteredParameterAnnotations.getOrDefault(element, NO_PARAMETER_ANNOTATIONS);
13651361
}
13661362

13671363
private static final TypeAnnotationValue[] NO_TYPE_ANNOTATIONS = new TypeAnnotationValue[0];
13681364

13691365
public TypeAnnotationValue[] getTypeAnnotationData(AnnotatedElement element) {
1370-
assert sealed;
1366+
assert isSealed();
13711367
return filteredTypeAnnotations.getOrDefault(element, NO_TYPE_ANNOTATIONS);
13721368
}
13731369

0 commit comments

Comments
 (0)