Skip to content

Commit bc958dc

Browse files
committed
Fixes for serialization reflection registration
1 parent 91267e3 commit bc958dc

File tree

1 file changed

+95
-21
lines changed

1 file changed

+95
-21
lines changed

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

Lines changed: 95 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.lang.invoke.SerializedLambda;
3535
import java.lang.reflect.Constructor;
3636
import java.lang.reflect.Field;
37+
import java.lang.reflect.InvocationTargetException;
3738
import java.lang.reflect.Member;
3839
import java.lang.reflect.Method;
3940
import java.lang.reflect.Modifier;
@@ -204,12 +205,8 @@ private static void registerLambdasFromConstantNodesInGraph(StructuredGraph grap
204205
Class<?> lambdaClass = getLambdaClassFromConstantNode(cNode);
205206

206207
if (lambdaClass != null && Serializable.class.isAssignableFrom(lambdaClass)) {
207-
try {
208-
Method serializeLambdaMethod = lambdaClass.getDeclaredMethod("writeReplace");
209-
RuntimeReflection.register(serializeLambdaMethod);
210-
} catch (NoSuchMethodException e) {
211-
throw VMError.shouldNotReachHere("Serializable lambda class must contain the writeReplace method.");
212-
}
208+
RuntimeReflection.register(ReflectionUtil.lookupMethod(lambdaClass, "writeReplace"));
209+
SerializationBuilder.registerSerializationUIDElements(lambdaClass, false);
213210
}
214211
}
215212
}
@@ -345,15 +342,20 @@ final class SerializationBuilder extends ConditionalConfigurationRegistry implem
345342
private final SerializationDenyRegistry denyRegistry;
346343
private final ConfigurationTypeResolver typeResolver;
347344
private final FeatureImpl.DuringSetupAccessImpl access;
345+
private final Method disableSerialConstructorChecks;
346+
private final Method superHasAccessibleConstructor;
348347
private boolean sealed;
349348
private final ProxyRegistry proxyRegistry;
350349

351350
SerializationBuilder(SerializationDenyRegistry serializationDenyRegistry, FeatureImpl.DuringSetupAccessImpl access, ConfigurationTypeResolver typeResolver, ProxyRegistry proxyRegistry) {
352351
this.access = access;
353352
Class<?> classDataSlotClazz = access.findClassByName("java.io.ObjectStreamClass$ClassDataSlot");
354-
descField = ReflectionUtil.lookupField(classDataSlotClazz, "desc");
355-
getDataLayoutMethod = ReflectionUtil.lookupMethod(ObjectStreamClass.class, "getClassDataLayout");
356-
stubConstructor = newConstructorForSerialization(SerializationSupport.StubForAbstractClass.class, null);
353+
this.descField = ReflectionUtil.lookupField(classDataSlotClazz, "desc");
354+
this.getDataLayoutMethod = ReflectionUtil.lookupMethod(ObjectStreamClass.class, "getClassDataLayout");
355+
this.stubConstructor = newConstructorForSerialization(SerializationSupport.StubForAbstractClass.class, null);
356+
this.disableSerialConstructorChecks = ReflectionUtil.lookupMethod(true, ReflectionFactory.class, "disableSerialConstructorChecks");
357+
this.superHasAccessibleConstructor = ReflectionUtil.lookupMethod(ReflectionFactory.class, "superHasAccessibleConstructor", Class.class);
358+
357359
this.denyRegistry = serializationDenyRegistry;
358360
this.typeResolver = typeResolver;
359361
this.proxyRegistry = proxyRegistry;
@@ -517,13 +519,41 @@ public void registerWithTargetConstructorClass(ConfigurationCondition condition,
517519
Optional.ofNullable(addConstructorAccessor(serializationTargetClass, customTargetConstructorClass))
518520
.map(ReflectionUtil::lookupConstructor)
519521
.ifPresent(RuntimeReflection::register);
522+
Class<?> superclass = serializationTargetClass.getSuperclass();
523+
if (superclass != null) {
524+
RuntimeReflection.registerAllDeclaredConstructors(superclass);
525+
RuntimeReflection.registerMethodLookup(superclass, "writeReplace");
526+
RuntimeReflection.registerMethodLookup(superclass, "readResolve");
527+
}
520528
registerForSerialization(serializationTargetClass);
521529
registerForDeserialization(serializationTargetClass);
522530
});
523531
}
524532
}
525533

526-
private static void registerForSerialization(Class<?> serializationTargetClass) {
534+
private static void registerQueriesForInheritableMethod(Class<?> clazz, String methodName, Class<?>... args) {
535+
Class<?> iter = clazz;
536+
while (iter != null) {
537+
RuntimeReflection.registerMethodLookup(iter, methodName, args);
538+
Method method = ReflectionUtil.lookupMethod(true, clazz, methodName, args);
539+
if (method != null) {
540+
RuntimeReflection.register(method);
541+
break;
542+
}
543+
iter = iter.getSuperclass();
544+
}
545+
}
546+
547+
private static void registerMethod(Class<?> clazz, String methodName, Class<?>... args) {
548+
Method method = ReflectionUtil.lookupMethod(true, clazz, methodName, args);
549+
if (method != null) {
550+
RuntimeReflection.register(method);
551+
} else {
552+
RuntimeReflection.registerMethodLookup(clazz, methodName, args);
553+
}
554+
}
555+
556+
private void registerForSerialization(Class<?> serializationTargetClass) {
527557

528558
/* Proxy classes have special treatment so no registration needed */
529559
if (Serializable.class.isAssignableFrom(serializationTargetClass) && !Proxy.isProxyClass(serializationTargetClass)) {
@@ -532,15 +562,62 @@ private static void registerForSerialization(Class<?> serializationTargetClass)
532562
* serialization class consistency, so need to register all constructors, methods and
533563
* fields.
534564
*/
565+
registerSerializationUIDElements(serializationTargetClass, true);
566+
567+
/*
568+
* Required by jdk.internal.reflect.ReflectionFactory.newConstructorForSerialization
569+
*/
570+
Class<?> initCl = serializationTargetClass;
571+
boolean initClValid = true;
572+
while (Serializable.class.isAssignableFrom(initCl)) {
573+
Class<?> prev = initCl;
574+
RuntimeReflection.registerAllDeclaredConstructors(initCl);
575+
try {
576+
if ((initCl = initCl.getSuperclass()) == null ||
577+
(!(boolean) disableSerialConstructorChecks.invoke(null) &&
578+
!prev.isArray() &&
579+
!(Boolean) superHasAccessibleConstructor.invoke(ReflectionFactory.getReflectionFactory(), prev))) {
580+
initClValid = false;
581+
break;
582+
}
583+
} catch (InvocationTargetException | IllegalAccessException e) {
584+
throw VMError.shouldNotReachHere(e);
585+
}
586+
}
587+
588+
if (initClValid) {
589+
RuntimeReflection.registerAllDeclaredConstructors(initCl);
590+
}
591+
592+
Class<?> iter = serializationTargetClass;
593+
while (iter != null) {
594+
Arrays.stream(iter.getDeclaredFields()).map(Field::getType).forEach(type -> {
595+
RuntimeReflection.registerAllDeclaredMethods(type);
596+
RuntimeReflection.registerAllDeclaredFields(type);
597+
RuntimeReflection.registerAllDeclaredConstructors(type);
598+
});
599+
iter = iter.getSuperclass();
600+
}
601+
}
602+
603+
registerQueriesForInheritableMethod(serializationTargetClass, "writeReplace");
604+
registerQueriesForInheritableMethod(serializationTargetClass, "readResolve");
605+
registerMethod(serializationTargetClass, "writeObject", ObjectOutputStream.class);
606+
registerMethod(serializationTargetClass, "readObjectNoData");
607+
registerMethod(serializationTargetClass, "readObject", ObjectInputStream.class);
608+
}
609+
610+
static void registerSerializationUIDElements(Class<?> serializationTargetClass, boolean fullyRegister) {
611+
RuntimeReflection.registerAllDeclaredConstructors(serializationTargetClass);
612+
RuntimeReflection.registerAllDeclaredMethods(serializationTargetClass);
613+
RuntimeReflection.registerAllDeclaredFields(serializationTargetClass);
614+
if (fullyRegister) {
615+
/* This is here a legacy that we can't remove as it is a breaking change */
535616
RuntimeReflection.register(serializationTargetClass.getDeclaredConstructors());
536617
RuntimeReflection.register(serializationTargetClass.getDeclaredMethods());
537618
RuntimeReflection.register(serializationTargetClass.getDeclaredFields());
538619
}
539-
540-
Optional.ofNullable(ReflectionUtil.lookupMethod(true, serializationTargetClass, "writeReplace"))
541-
.ifPresent(RuntimeReflection::register);
542-
Optional.ofNullable(ReflectionUtil.lookupMethod(true, serializationTargetClass, "writeObject", ObjectOutputStream.class))
543-
.ifPresent(RuntimeReflection::register);
620+
RuntimeReflection.registerFieldLookup(serializationTargetClass, "serialPersistentFields");
544621
}
545622

546623
public void afterAnalysis() {
@@ -561,14 +638,11 @@ private static void registerForDeserialization(Class<?> serializationTargetClass
561638
RuntimeReflection.registerAllRecordComponents(serializationTargetClass);
562639
RuntimeReflection.register(RecordUtils.getRecordComponentAccessorMethods(serializationTargetClass));
563640
} else if (Externalizable.class.isAssignableFrom(serializationTargetClass)) {
564-
Optional.ofNullable(ReflectionUtil.lookupConstructor(true, serializationTargetClass, (Class<?>[]) null))
565-
.ifPresent(RuntimeReflection::register);
641+
RuntimeReflection.registerConstructorLookup(serializationTargetClass);
566642
}
567643

568-
Optional.ofNullable(ReflectionUtil.lookupMethod(true, serializationTargetClass, "readObject", ObjectInputStream.class))
569-
.ifPresent(RuntimeReflection::register);
570-
Optional.ofNullable(ReflectionUtil.lookupMethod(true, serializationTargetClass, "readResolve"))
571-
.ifPresent(RuntimeReflection::register);
644+
registerMethod(serializationTargetClass, "readObject", ObjectInputStream.class);
645+
registerMethod(serializationTargetClass, "readResolve");
572646
}
573647

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

0 commit comments

Comments
 (0)