3434import java .lang .invoke .SerializedLambda ;
3535import java .lang .reflect .Constructor ;
3636import java .lang .reflect .Field ;
37+ import java .lang .reflect .InvocationTargetException ;
3738import java .lang .reflect .Member ;
3839import java .lang .reflect .Method ;
3940import 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