Skip to content

Commit 3984679

Browse files
committed
Layered serialization support
1 parent 381adbb commit 3984679

12 files changed

+148
-58
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123
import com.oracle.svm.core.reflect.RuntimeMetadataDecoder.FieldDescriptor;
124124
import com.oracle.svm.core.reflect.RuntimeMetadataDecoder.MethodDescriptor;
125125
import com.oracle.svm.core.reflect.fieldaccessor.UnsafeFieldAccessorFactory;
126-
import com.oracle.svm.core.reflect.serialize.SerializationRegistry;
126+
import com.oracle.svm.core.reflect.serialize.SerializationSupport;
127127
import com.oracle.svm.core.reflect.target.Target_jdk_internal_reflect_ConstantPool;
128128
import com.oracle.svm.core.util.LazyFinalReference;
129129
import com.oracle.svm.core.util.VMError;
@@ -1140,7 +1140,7 @@ public boolean isLinked() {
11401140
}
11411141

11421142
public boolean isRegisteredForSerialization() {
1143-
return ImageSingletons.lookup(SerializationRegistry.class).isRegisteredForSerialization(DynamicHub.toClass(this));
1143+
return SerializationSupport.isRegisteredForSerialization(this);
11441144
}
11451145

11461146
@KeepOriginal
@@ -2335,8 +2335,7 @@ public FieldAccessor newFieldAccessor(Field field0, boolean override) {
23352335

23362336
@Substitute
23372337
private Constructor<?> generateConstructor(Class<?> cl, Constructor<?> constructorToCall) {
2338-
SerializationRegistry serializationRegistry = ImageSingletons.lookup(SerializationRegistry.class);
2339-
ConstructorAccessor acc = (ConstructorAccessor) serializationRegistry.getSerializationConstructorAccessor(cl, constructorToCall.getDeclaringClass());
2338+
ConstructorAccessor acc = (ConstructorAccessor) SerializationSupport.getSerializationConstructorAccessor(cl, constructorToCall.getDeclaringClass());
23402339
/*
23412340
* Unlike other root constructors, this constructor is not copied for mutation but directly
23422341
* mutated, as it is not cached. To cache this constructor, setAccessible call must be done

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ private static void registerLambdaForReflection(Class<?> lambdaClass) {
162162
* lambda-class information from the capturing class.
163163
*/
164164
if (Serializable.class.isAssignableFrom(lambdaClass) &&
165-
SerializationSupport.singleton().isLambdaCapturingClassRegistered(LambdaUtils.capturingClass(lambdaClass.getName()))) {
165+
SerializationSupport.currentLayer().isLambdaCapturingClassRegistered(LambdaUtils.capturingClass(lambdaClass.getName()))) {
166166
try {
167167
Method serializeLambdaMethod = lambdaClass.getDeclaredMethod("writeReplace");
168168
RuntimeReflection.register(serializeLambdaMethod);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationRegistry.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
*/
2626
package com.oracle.svm.core.reflect.serialize;
2727

28-
public interface SerializationRegistry {
29-
30-
boolean isRegisteredForSerialization(Class<?> cl);
28+
import com.oracle.svm.core.hub.DynamicHub;
3129

32-
Object getSerializationConstructorAccessor(Class<?> serializationTargetClass, Class<?> targetConstructorClass);
30+
public interface SerializationRegistry {
31+
boolean isRegisteredForSerialization0(DynamicHub dynamicHub);
3332

33+
Object getSerializationConstructorAccessor0(Class<?> declaringClass, Class<?> targetConstructorClass);
3434
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java

Lines changed: 92 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,38 @@
3131
import java.lang.invoke.SerializedLambda;
3232
import java.lang.reflect.Constructor;
3333
import java.lang.reflect.Modifier;
34+
import java.util.EnumSet;
3435
import java.util.Objects;
3536

3637
import org.graalvm.collections.EconomicMap;
3738
import org.graalvm.collections.MapCursor;
38-
import org.graalvm.nativeimage.ImageSingletons;
3939
import org.graalvm.nativeimage.Platform;
4040
import org.graalvm.nativeimage.Platforms;
4141
import org.graalvm.nativeimage.impl.ConfigurationCondition;
4242

43+
import com.oracle.svm.core.BuildPhaseProvider;
44+
import com.oracle.svm.core.SubstrateUtil;
4345
import com.oracle.svm.core.configure.RuntimeConditionSet;
46+
import com.oracle.svm.core.hub.DynamicHub;
47+
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
48+
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
49+
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
50+
import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton;
4451
import com.oracle.svm.core.reflect.SubstrateConstructorAccessor;
4552
import com.oracle.svm.core.util.ImageHeapMap;
4653
import com.oracle.svm.core.util.VMError;
4754

4855
import jdk.graal.compiler.java.LambdaUtils;
4956

50-
public class SerializationSupport implements SerializationRegistry {
57+
public class SerializationSupport implements MultiLayeredImageSingleton, SerializationRegistry, UnsavedSingleton {
5158

52-
public static SerializationSupport singleton() {
53-
return (SerializationSupport) ImageSingletons.lookup(SerializationRegistry.class);
59+
@Platforms(Platform.HOSTED_ONLY.class)
60+
public static SerializationSupport currentLayer() {
61+
return LayeredImageSingletonSupport.singleton().lookup(SerializationSupport.class, false, true);
62+
}
63+
64+
public static SerializationSupport[] layeredSingletons() {
65+
return MultiLayeredImageSingleton.getAllLayers(SerializationSupport.class);
5466
}
5567

5668
/**
@@ -184,19 +196,48 @@ public String getClassLoaderSerializationLookupKey(ClassLoader classLoader) {
184196
throw VMError.shouldNotReachHere("No constructor accessor uses the class loader %s", classLoader);
185197
}
186198

187-
private final EconomicMap<Class<?>, RuntimeConditionSet> classes = EconomicMap.create();
199+
/**
200+
* This class is used as key in maps that use {@link Class} as key at runtime in layered images,
201+
* because the hash code of {@link Class} objects cannot be injected in extension layers and is
202+
* thus inconsistent across layers. The state of those maps is then incorrect at run time. The
203+
* {@link DynamicHub} cannot be used directly either as its hash code at run time is the one of
204+
* the {@link Class} object.
205+
* <p>
206+
* Temporary key for maps ideally indexed by their {@link Class} or {@link DynamicHub}. At
207+
* runtime, these maps should be indexed by {@link DynamicHub#getTypeID}
208+
*/
209+
public record DynamicHubKey(DynamicHub hub) {
210+
public int getTypeID() {
211+
return hub.getTypeID();
212+
}
213+
}
214+
215+
private final EconomicMap<Object /* DynamicHubKey or DynamicHub.typeID */, RuntimeConditionSet> classes = EconomicMap.create();
188216
private final EconomicMap<String, RuntimeConditionSet> lambdaCapturingClasses = EconomicMap.create();
189217

190218
@Platforms(Platform.HOSTED_ONLY.class)
191-
public void registerSerializationTargetClass(ConfigurationCondition cnd, Class<?> serializationTargetClass) {
219+
public void registerSerializationTargetClass(ConfigurationCondition cnd, DynamicHub hub) {
192220
synchronized (classes) {
193-
var previous = classes.putIfAbsent(serializationTargetClass, RuntimeConditionSet.createHosted(cnd));
221+
var previous = classes.putIfAbsent(BuildPhaseProvider.isHostedUniverseBuilt() ? hub.getTypeID() : new DynamicHubKey(hub), RuntimeConditionSet.createHosted(cnd));
194222
if (previous != null) {
195223
previous.addCondition(cnd);
196224
}
197225
}
198226
}
199227

228+
public void replaceHubKeyWithTypeID() {
229+
EconomicMap<Integer, RuntimeConditionSet> newEntries = EconomicMap.create();
230+
var cursor = classes.getEntries();
231+
while (cursor.advance()) {
232+
Object key = cursor.getKey();
233+
if (key instanceof DynamicHubKey hubKey) {
234+
newEntries.put(hubKey.getTypeID(), cursor.getValue());
235+
cursor.remove();
236+
}
237+
}
238+
classes.putAll(newEntries);
239+
}
240+
200241
@Platforms(Platform.HOSTED_ONLY.class)
201242
public void registerLambdaCapturingClass(ConfigurationCondition cnd, String lambdaCapturingClass) {
202243
synchronized (lambdaCapturingClasses) {
@@ -212,37 +253,63 @@ public boolean isLambdaCapturingClassRegistered(String lambdaCapturingClass) {
212253
return lambdaCapturingClasses.containsKey(lambdaCapturingClass);
213254
}
214255

215-
@Override
216-
public Object getSerializationConstructorAccessor(Class<?> rawDeclaringClass, Class<?> rawTargetConstructorClass) {
217-
Class<?> declaringClass = rawDeclaringClass;
256+
public static Object getSerializationConstructorAccessor(Class<?> serializationTargetClass, Class<?> targetConstructorClass) {
257+
Class<?> declaringClass = serializationTargetClass;
218258

219259
if (LambdaUtils.isLambdaClass(declaringClass)) {
220260
declaringClass = SerializedLambda.class;
221261
}
222262

263+
if (SubstrateUtil.HOSTED) {
264+
Object constructorAccessor = currentLayer().getSerializationConstructorAccessor0(declaringClass, targetConstructorClass);
265+
if (constructorAccessor != null) {
266+
return constructorAccessor;
267+
}
268+
} else {
269+
for (var singleton : layeredSingletons()) {
270+
Object constructorAccessor = singleton.getSerializationConstructorAccessor0(declaringClass, targetConstructorClass);
271+
if (constructorAccessor != null) {
272+
return constructorAccessor;
273+
}
274+
}
275+
}
276+
277+
String targetConstructorClassName = targetConstructorClass.getName();
278+
if (ThrowMissingRegistrationErrors.hasBeenSet()) {
279+
MissingSerializationRegistrationUtils.missingSerializationRegistration(declaringClass,
280+
"type " + declaringClass.getTypeName() + " with target constructor class: " + targetConstructorClassName);
281+
} else {
282+
throw VMError.unsupportedFeature("SerializationConstructorAccessor class not found for declaringClass: " + declaringClass.getName() +
283+
" (targetConstructorClass: " + targetConstructorClassName + "). Usually adding " + declaringClass.getName() +
284+
" to serialization-config.json fixes the problem.");
285+
}
286+
return null;
287+
}
288+
289+
@Override
290+
public Object getSerializationConstructorAccessor0(Class<?> declaringClass, Class<?> rawTargetConstructorClass) {
223291
VMError.guarantee(stubConstructor != null, "Called too early, no stub constructor yet.");
224292
Class<?> targetConstructorClass = Modifier.isAbstract(declaringClass.getModifiers()) ? stubConstructor.getDeclaringClass() : rawTargetConstructorClass;
225-
Object constructorAccessor = constructorAccessors.get(new SerializationLookupKey(declaringClass, targetConstructorClass));
293+
return constructorAccessors.get(new SerializationLookupKey(declaringClass, targetConstructorClass));
294+
}
226295

227-
if (constructorAccessor != null) {
228-
return constructorAccessor;
229-
} else {
230-
String targetConstructorClassName = targetConstructorClass.getName();
231-
if (ThrowMissingRegistrationErrors.hasBeenSet()) {
232-
MissingSerializationRegistrationUtils.missingSerializationRegistration(declaringClass,
233-
"type " + declaringClass.getTypeName() + " with target constructor class: " + targetConstructorClassName);
234-
} else {
235-
throw VMError.unsupportedFeature("SerializationConstructorAccessor class not found for declaringClass: " + declaringClass.getName() +
236-
" (targetConstructorClass: " + targetConstructorClassName + "). Usually adding " + declaringClass.getName() +
237-
" to serialization-config.json fixes the problem.");
296+
public static boolean isRegisteredForSerialization(DynamicHub hub) {
297+
for (SerializationRegistry singleton : SerializationSupport.layeredSingletons()) {
298+
if (singleton.isRegisteredForSerialization0(hub)) {
299+
return true;
238300
}
239-
return null;
240301
}
302+
return false;
241303
}
242304

243305
@Override
244-
public boolean isRegisteredForSerialization(Class<?> clazz) {
245-
var conditionSet = classes.get(clazz);
306+
public boolean isRegisteredForSerialization0(DynamicHub dynamicHub) {
307+
var conditionSet = classes.get(dynamicHub.getTypeID());
246308
return conditionSet != null && conditionSet.satisfied();
247309
}
310+
311+
@Override
312+
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
313+
return LayeredImageSingletonBuilderFlags.ALL_ACCESS;
314+
}
248315
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
public abstract class ConditionalConfigurationRegistry {
4141
private Feature.BeforeAnalysisAccess beforeAnalysisAccess;
42+
private SVMHost hostVM;
4243
private final Map<Class<?>, Collection<Runnable>> pendingReachabilityHandlers = new ConcurrentHashMap<>();
4344

4445
protected void registerConditionalConfiguration(ConfigurationCondition condition, Consumer<ConfigurationCondition> consumer) {
@@ -82,4 +83,12 @@ public void setAnalysisAccess(Feature.BeforeAnalysisAccess beforeAnalysisAccess)
8283
pendingReachabilityHandlers.clear();
8384
}
8485

86+
public void setHostVM(SVMHost hostVM) {
87+
this.hostVM = hostVM;
88+
}
89+
90+
public SVMHost getHostVM() {
91+
return hostVM;
92+
}
93+
8594
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
import org.graalvm.nativeimage.impl.AnnotationExtractor;
7474
import org.graalvm.nativeimage.impl.CConstantValueSupport;
7575
import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;
76+
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
77+
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
7678
import org.graalvm.nativeimage.impl.SizeOfSupport;
7779
import org.graalvm.word.PointerBase;
7880

@@ -1029,11 +1031,13 @@ protected void setupNativeImage(String imageName, OptionValues options, Map<Meth
10291031
if (imageLayerLoader != null) {
10301032
imageLayerLoader.setHostedValuesProvider(hostedValuesProvider);
10311033
}
1034+
SVMHost hostVM = (SVMHost) aUniverse.hostVM();
1035+
((ConditionalConfigurationRegistry) ImageSingletons.lookup(RuntimeSerializationSupport.class)).setHostVM(hostVM);
10321036
SubstratePlatformConfigurationProvider platformConfig = getPlatformConfig(aMetaAccess);
10331037
HostedProviders aProviders = createHostedProviders(target, aUniverse, originalProviders, platformConfig, aMetaAccess, classInitializationSupport);
10341038
aUniverse.hostVM().initializeProviders(aProviders);
10351039

1036-
ImageSingletons.add(SimulateClassInitializerSupport.class, ((SVMHost) aUniverse.hostVM()).createSimulateClassInitializerSupport(aMetaAccess));
1040+
ImageSingletons.add(SimulateClassInitializerSupport.class, (hostVM).createSimulateClassInitializerSupport(aMetaAccess));
10371041

10381042
bb = createBigBang(debug, options, aUniverse, aMetaAccess, aProviders, annotationSubstitutions);
10391043
aUniverse.setBigBang(bb);
@@ -1098,7 +1102,7 @@ protected void setupNativeImage(String imageName, OptionValues options, Map<Meth
10981102
}
10991103

11001104
initializeBigBang(bb, options, featureHandler, nativeLibraries, debug, aMetaAccess, aUniverse.getSubstitutions(), loader, true,
1101-
new SubstrateClassInitializationPlugin((SVMHost) aUniverse.hostVM()), this.isStubBasedPluginsSupported(), aProviders);
1105+
new SubstrateClassInitializationPlugin(hostVM), this.isStubBasedPluginsSupported(), aProviders);
11021106

11031107
loader.classLoaderSupport.getClassesToIncludeUnconditionally().forEach(cls -> bb.registerTypeForBaseImage(cls));
11041108
PreserveOptionsSupport.registerPreservedClasses(loader.classLoaderSupport);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/RuntimeMetadataEncoderImpl.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ private void registerClass(HostedType type, ClassMetadata metadata) {
203203

204204
private void registerField(HostedType declaringType, Object field, FieldMetadata metadata) {
205205
if (ImageLayerBuildingSupport.buildingImageLayer() &&
206-
!LayeredRuntimeMetadataSingleton.singleton().registerField(field, declaringType.getWrapped().getUniverse().getBigbang().getMetaAccess())) {
206+
!LayeredRuntimeMetadataSingleton.singleton().shouldRegisterField(field, declaringType.getWrapped().getUniverse().getBigbang().getMetaAccess(), metadata)) {
207207
return;
208208
}
209209
addType(declaringType);
@@ -219,7 +219,7 @@ private FieldMetadata[] getFields(HostedType declaringType) {
219219

220220
private void registerMethod(HostedType declaringType, Object method, MethodMetadata metadata) {
221221
if (ImageLayerBuildingSupport.buildingImageLayer() &&
222-
!LayeredRuntimeMetadataSingleton.singleton().registerMethod(method, declaringType.getWrapped().getUniverse().getBigbang().getMetaAccess())) {
222+
!LayeredRuntimeMetadataSingleton.singleton().shouldRegisterMethod(method, declaringType.getWrapped().getUniverse().getBigbang().getMetaAccess(), metadata)) {
223223
return;
224224
}
225225
addType(declaringType);
@@ -235,7 +235,7 @@ private MethodMetadata[] getMethods(HostedType declaringType) {
235235

236236
private void registerConstructor(HostedType declaringType, Object constructor, ConstructorMetadata metadata) {
237237
if (ImageLayerBuildingSupport.buildingImageLayer() &&
238-
!LayeredRuntimeMetadataSingleton.singleton().registerMethod(constructor, declaringType.getWrapped().getUniverse().getBigbang().getMetaAccess())) {
238+
!LayeredRuntimeMetadataSingleton.singleton().shouldRegisterMethod(constructor, declaringType.getWrapped().getUniverse().getBigbang().getMetaAccess(), metadata)) {
239239
return;
240240
}
241241
addType(declaringType);
@@ -1249,7 +1249,15 @@ private static LayeredRuntimeMetadataSingleton singleton() {
12491249
return ImageSingletons.lookup(LayeredRuntimeMetadataSingleton.class);
12501250
}
12511251

1252-
public boolean registerMethod(Object method, AnalysisMetaAccess metaAccess) {
1252+
public boolean shouldRegisterMethod(Object method, AnalysisMetaAccess metaAccess, AccessibleObjectMetadata metadata) {
1253+
if (!metadata.complete) {
1254+
/*
1255+
* The method should be added to the list of registered methods only if the metadata
1256+
* is complete. Incomplete metadata should always be registered and should not be
1257+
* counted as the only metadata registered for a given method.
1258+
*/
1259+
return true;
1260+
}
12531261
if (method instanceof AnalysisMethod analysisMethod) {
12541262
return registeredMethods.add(analysisMethod.getId());
12551263
} else if (method instanceof HostedMethod hostedMethod) {
@@ -1262,7 +1270,10 @@ public boolean registerMethod(Object method, AnalysisMetaAccess metaAccess) {
12621270
return true;
12631271
}
12641272

1265-
public boolean registerField(Object field, AnalysisMetaAccess metaAccess) {
1273+
public boolean shouldRegisterField(Object field, AnalysisMetaAccess metaAccess, AccessibleObjectMetadata metadata) {
1274+
if (!metadata.complete) {
1275+
return true;
1276+
}
12661277
if (field instanceof AnalysisField analysisField) {
12671278
return registeredFields.add(analysisField.getId());
12681279
} else if (field instanceof HostedField hostedField) {

0 commit comments

Comments
 (0)