Skip to content

Commit f8cfda4

Browse files
committed
Track preserved state through serialization
1 parent c2ea8b8 commit f8cfda4

File tree

2 files changed

+74
-51
lines changed

2 files changed

+74
-51
lines changed

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,12 @@ public int getTypeID() {
219219
private final EconomicMap<String, RuntimeConditionSet> lambdaCapturingClasses = EconomicMap.create();
220220

221221
@Platforms(Platform.HOSTED_ONLY.class)
222-
public void registerSerializationTargetClass(AccessCondition cnd, DynamicHub hub) {
222+
public void registerSerializationTargetClass(AccessCondition cnd, DynamicHub hub, boolean preserved) {
223223
synchronized (classes) {
224-
var previous = classes.putIfAbsent(BuildPhaseProvider.isHostedUniverseBuilt() ? hub.getTypeID() : new DynamicHubKey(hub), RuntimeConditionSet.createHosted(cnd, false));
224+
var previous = classes.putIfAbsent(BuildPhaseProvider.isHostedUniverseBuilt() ? hub.getTypeID() : new DynamicHubKey(hub), RuntimeConditionSet.createHosted(cnd, preserved));
225225
if (previous != null) {
226226
previous.addCondition(cnd);
227+
previous.reportReregistered(preserved);
227228
}
228229
}
229230
}
@@ -313,4 +314,14 @@ public boolean isRegisteredForSerialization0(DynamicHub dynamicHub) {
313314
var conditionSet = classes.get(dynamicHub.getTypeID());
314315
return conditionSet != null && conditionSet.satisfied();
315316
}
317+
318+
public static boolean isPreservedForSerialization(DynamicHub dynamicHub) {
319+
for (SerializationSupport singleton : SerializationSupport.layeredSingletons()) {
320+
var conditionSet = singleton.classes.get(dynamicHub.getTypeID());
321+
if (conditionSet != null) {
322+
return conditionSet.preserved();
323+
}
324+
}
325+
return false;
326+
}
316327
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java

Lines changed: 61 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@ private static void registerLambdasFromConstantNodesInGraph(StructuredGraph grap
155155

156156
if (lambdaClass != null && Serializable.class.isAssignableFrom(lambdaClass)) {
157157
RuntimeReflection.register(ReflectionUtil.lookupMethod(lambdaClass, "writeReplace"));
158-
SerializationBuilder.registerSerializationUIDElements(lambdaClass, false);
159-
serializationBuilder.serializationSupport.registerSerializationTargetClass(AccessCondition.unconditional(), serializationBuilder.getHostVM().dynamicHub(lambdaClass));
158+
SerializationBuilder.registerSerializationUIDElements(lambdaClass, false, false);
159+
serializationBuilder.serializationSupport.registerSerializationTargetClass(AccessCondition.unconditional(), serializationBuilder.getHostVM().dynamicHub(lambdaClass), false);
160160
}
161161
}
162162
}
@@ -423,10 +423,10 @@ public void register(AccessCondition condition, boolean preserved, Class<?> seri
423423
* Making this class reachable as it will end up in the image heap without the analysis
424424
* knowing.
425425
*/
426-
RuntimeReflection.register(java.io.ObjectOutputStream.class);
426+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, preserved, java.io.ObjectOutputStream.class);
427427

428428
if (denyRegistry.isAllowed(serializationTargetClass)) {
429-
addOrQueueConstructorAccessors(cnd, serializationTargetClass, getHostVM().dynamicHub(serializationTargetClass));
429+
addOrQueueConstructorAccessors(cnd, serializationTargetClass, getHostVM().dynamicHub(serializationTargetClass), preserved);
430430

431431
Class<?> superclass = serializationTargetClass.getSuperclass();
432432
if (superclass != null) {
@@ -435,35 +435,35 @@ public void register(AccessCondition condition, boolean preserved, Class<?> seri
435435
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerMethodLookup(AccessCondition.unconditional(), preserved, superclass, "readResolve");
436436
}
437437

438-
registerForSerialization(cnd, serializationTargetClass);
439-
registerForDeserialization(cnd, serializationTargetClass);
438+
registerForSerialization(cnd, preserved, serializationTargetClass);
439+
registerForDeserialization(cnd, preserved, serializationTargetClass);
440440

441441
}
442442
});
443443
}
444444

445-
private void addOrQueueConstructorAccessors(AccessCondition cnd, Class<?> serializationTargetClass, DynamicHub hub) {
445+
private void addOrQueueConstructorAccessors(AccessCondition cnd, Class<?> serializationTargetClass, DynamicHub hub, boolean preserved) {
446446
if (pendingConstructorRegistrations != null) {
447447
// cannot yet create constructor accessor -> add to pending
448-
pendingConstructorRegistrations.add(() -> registerConstructorAccessors(cnd, serializationTargetClass, hub));
448+
pendingConstructorRegistrations.add(() -> registerConstructorAccessors(cnd, serializationTargetClass, hub, preserved));
449449
} else {
450450
// can already run the registration
451-
registerConstructorAccessors(cnd, serializationTargetClass, hub);
451+
registerConstructorAccessors(cnd, serializationTargetClass, hub, preserved);
452452
}
453453
}
454454

455-
private void registerConstructorAccessors(AccessCondition cnd, Class<?> serializationTargetClass, DynamicHub hub) {
456-
serializationSupport.registerSerializationTargetClass(cnd, hub);
457-
registerConstructorAccessor(cnd, serializationTargetClass, null);
455+
private void registerConstructorAccessors(AccessCondition cnd, Class<?> serializationTargetClass, DynamicHub hub, boolean preserved) {
456+
serializationSupport.registerSerializationTargetClass(cnd, hub, preserved);
457+
registerConstructorAccessor(cnd, serializationTargetClass, null, preserved);
458458
for (Class<?> superclass = serializationTargetClass; superclass != null; superclass = superclass.getSuperclass()) {
459-
registerConstructorAccessor(cnd, serializationTargetClass, superclass);
459+
registerConstructorAccessor(cnd, serializationTargetClass, superclass, preserved);
460460
}
461461
}
462462

463-
private void registerConstructorAccessor(AccessCondition cnd, Class<?> serializationTargetClass, Class<?> targetConstructorClass) {
463+
private void registerConstructorAccessor(AccessCondition cnd, Class<?> serializationTargetClass, Class<?> targetConstructorClass, boolean preserved) {
464464
Optional.ofNullable(addConstructorAccessor(serializationTargetClass, targetConstructorClass))
465465
.map(ReflectionUtil::lookupConstructor)
466-
.ifPresent(methods -> ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, false, methods));
466+
.ifPresent(methods -> ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, preserved, methods));
467467
}
468468

469469
void beforeAnalysis(Feature.BeforeAnalysisAccess beforeAnalysisAccess) {
@@ -474,37 +474,37 @@ void beforeAnalysis(Feature.BeforeAnalysisAccess beforeAnalysisAccess) {
474474
serializationSupport.setStubConstructor(stubConstructor);
475475
}
476476

477-
private static void registerQueriesForInheritableMethod(Class<?> clazz, String methodName, Class<?>... args) {
477+
private static void registerQueriesForInheritableMethod(boolean preserved, Class<?> clazz, String methodName, Class<?>... args) {
478478
Class<?> iter = clazz;
479479
while (iter != null) {
480-
RuntimeReflection.registerMethodLookup(iter, methodName, args);
480+
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerMethodLookup(AccessCondition.unconditional(), preserved, iter, methodName, args);
481481
Method method = ReflectionUtil.lookupMethod(true, clazz, methodName, args);
482482
if (method != null) {
483-
RuntimeReflection.register(method);
483+
registerMethod(AccessCondition.unconditional(), preserved, clazz, methodName, args);
484484
break;
485485
}
486486
iter = iter.getSuperclass();
487487
}
488488
}
489489

490-
private static void registerMethod(AccessCondition cnd, Class<?> clazz, String methodName, Class<?>... args) {
490+
private static void registerMethod(AccessCondition cnd, boolean preserved, Class<?> clazz, String methodName, Class<?>... args) {
491491
Method method = ReflectionUtil.lookupMethod(true, clazz, methodName, args);
492492
if (method != null) {
493-
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, false, method);
493+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, preserved, method);
494494
} else {
495-
RuntimeReflection.registerMethodLookup(clazz, methodName, args);
495+
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerMethodLookup(AccessCondition.unconditional(), preserved, clazz, methodName, args);
496496
}
497497
}
498498

499-
private void registerForSerialization(AccessCondition cnd, Class<?> serializationTargetClass) {
499+
private void registerForSerialization(AccessCondition cnd, boolean preserved, Class<?> serializationTargetClass) {
500500

501501
if (Serializable.class.isAssignableFrom(serializationTargetClass)) {
502502
/*
503503
* ObjectStreamClass.computeDefaultSUID is always called at runtime to verify
504504
* serialization class consistency, so need to register all constructors, methods and
505505
* fields.
506506
*/
507-
registerSerializationUIDElements(serializationTargetClass, true); // if MRE
507+
registerSerializationUIDElements(serializationTargetClass, true, preserved); // if MRE
508508

509509
/*
510510
* Required by jdk.internal.reflect.ReflectionFactory.newConstructorForSerialization
@@ -513,7 +513,7 @@ private void registerForSerialization(AccessCondition cnd, Class<?> serializatio
513513
boolean initClValid = true;
514514
while (Serializable.class.isAssignableFrom(initCl)) {
515515
Class<?> prev = initCl;
516-
RuntimeReflection.registerAllDeclaredConstructors(initCl);
516+
registerAllDeclaredConstructors(preserved, initCl);
517517
if ((initCl = initCl.getSuperclass()) == null || (!disableSerialConstructorChecks() &&
518518
!prev.isArray() && !superHasAccessibleConstructor(prev))) {
519519
initClValid = false;
@@ -522,18 +522,18 @@ private void registerForSerialization(AccessCondition cnd, Class<?> serializatio
522522
}
523523

524524
if (initClValid) {
525-
RuntimeReflection.registerAllDeclaredConstructors(initCl);
525+
registerAllDeclaredConstructors(preserved, initCl);
526526
}
527527

528528
Class<?> iter = serializationTargetClass;
529529
while (iter != null) {
530-
RuntimeReflection.registerAllDeclaredFields(iter);
530+
registerAllDeclaredFields(preserved, iter);
531531
try {
532532
Arrays.stream(iter.getDeclaredFields())
533533
.map(Field::getType).forEach(type -> {
534-
RuntimeReflection.registerAllDeclaredMethods(type);
535-
RuntimeReflection.registerAllDeclaredFields(type);
536-
RuntimeReflection.registerAllDeclaredConstructors(type);
534+
registerAllDeclaredMethods(preserved, type);
535+
registerAllDeclaredFields(preserved, type);
536+
registerAllDeclaredConstructors(preserved, type);
537537
});
538538
} catch (LinkageError l) {
539539
/* Handled with registration above */
@@ -542,37 +542,49 @@ private void registerForSerialization(AccessCondition cnd, Class<?> serializatio
542542
}
543543
}
544544

545-
registerQueriesForInheritableMethod(serializationTargetClass, "writeReplace");
546-
registerQueriesForInheritableMethod(serializationTargetClass, "readResolve");
547-
registerMethod(cnd, serializationTargetClass, "writeObject", ObjectOutputStream.class);
548-
registerMethod(cnd, serializationTargetClass, "readObjectNoData");
549-
registerMethod(cnd, serializationTargetClass, "readObject", ObjectInputStream.class);
545+
registerQueriesForInheritableMethod(preserved, serializationTargetClass, "writeReplace");
546+
registerQueriesForInheritableMethod(preserved, serializationTargetClass, "readResolve");
547+
registerMethod(cnd, preserved, serializationTargetClass, "writeObject", ObjectOutputStream.class);
548+
registerMethod(cnd, preserved, serializationTargetClass, "readObjectNoData");
549+
registerMethod(cnd, preserved, serializationTargetClass, "readObject", ObjectInputStream.class);
550550
}
551551

552552
@SuppressWarnings("unused")
553-
static void registerSerializationUIDElements(Class<?> serializationTargetClass, boolean fullyRegister) {
554-
RuntimeReflection.registerAllDeclaredConstructors(serializationTargetClass);
555-
RuntimeReflection.registerAllDeclaredMethods(serializationTargetClass);
556-
RuntimeReflection.registerAllDeclaredFields(serializationTargetClass);
553+
static void registerSerializationUIDElements(Class<?> serializationTargetClass, boolean fullyRegister, boolean preserved) {
554+
registerAllDeclaredConstructors(preserved, serializationTargetClass);
555+
registerAllDeclaredMethods(preserved, serializationTargetClass);
556+
registerAllDeclaredFields(preserved, serializationTargetClass);
557557
if (fullyRegister) {
558558
try {
559559
/* This is here a legacy that we can't remove as it is a breaking change */
560-
RuntimeReflection.register(serializationTargetClass.getDeclaredConstructors());
561-
RuntimeReflection.register(serializationTargetClass.getDeclaredMethods());
562-
RuntimeReflection.register(serializationTargetClass.getDeclaredFields());
560+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, preserved, serializationTargetClass.getDeclaredConstructors());
561+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, preserved, serializationTargetClass.getDeclaredMethods());
562+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, preserved, serializationTargetClass.getDeclaredFields());
563563
} catch (LinkageError e) {
564564
/* Handled by registrations above */
565565
}
566566
}
567-
RuntimeReflection.registerFieldLookup(serializationTargetClass, "serialPersistentFields");
567+
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerFieldLookup(AccessCondition.unconditional(), preserved, serializationTargetClass, "serialPersistentFields");
568+
}
569+
570+
static void registerAllDeclaredConstructors(boolean preserved, Class<?> declaringClass) {
571+
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredConstructorsQuery(AccessCondition.unconditional(), true, preserved, declaringClass);
572+
}
573+
574+
static void registerAllDeclaredMethods(boolean preserved, Class<?> declaringClass) {
575+
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredMethodsQuery(AccessCondition.unconditional(), true, preserved, declaringClass);
576+
}
577+
578+
static void registerAllDeclaredFields(boolean preserved, Class<?> declaringClass) {
579+
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredFields(AccessCondition.unconditional(), preserved, declaringClass);
568580
}
569581

570582
public void afterAnalysis() {
571583
sealed();
572584
}
573585

574-
private static void registerForDeserialization(AccessCondition cnd, Class<?> serializationTargetClass) {
575-
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, serializationTargetClass);
586+
private static void registerForDeserialization(AccessCondition cnd, boolean preserved, Class<?> serializationTargetClass) {
587+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, preserved, serializationTargetClass);
576588

577589
if (serializationTargetClass.isRecord()) {
578590
/*
@@ -584,20 +596,20 @@ private static void registerForDeserialization(AccessCondition cnd, Class<?> ser
584596
try {
585597
/* Serialization for records uses the canonical record constructor directly. */
586598
Executable[] methods = new Executable[]{RecordUtils.getCanonicalRecordConstructor(serializationTargetClass)};
587-
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, false, methods);
599+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, preserved, methods);
588600
Executable[] methods1 = RecordUtils.getRecordComponentAccessorMethods(serializationTargetClass);
589-
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, false, methods1);
601+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, preserved, methods1);
590602
} catch (LinkageError le) {
591603
/*
592604
* Handled by the record component registration above.
593605
*/
594606
}
595607
} else if (Externalizable.class.isAssignableFrom(serializationTargetClass)) {
596-
RuntimeReflection.registerConstructorLookup(serializationTargetClass);
608+
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerConstructorLookup(AccessCondition.unconditional(), preserved, serializationTargetClass);
597609
}
598610

599-
registerMethod(cnd, serializationTargetClass, "readObject", ObjectInputStream.class);
600-
registerMethod(cnd, serializationTargetClass, "readResolve");
611+
registerMethod(cnd, preserved, serializationTargetClass, "readObject", ObjectInputStream.class);
612+
registerMethod(cnd, preserved, serializationTargetClass, "readResolve");
601613
}
602614

603615
private Constructor<?> newConstructorForSerialization(Class<?> serializationTargetClass, Constructor<?> customConstructorToCall) {

0 commit comments

Comments
 (0)