Skip to content

Commit 800b8b5

Browse files
shanshinSpace Team
authored andcommitted
[KxSerialization] Fixed initialization error for keep generated serializer feature with sealed classes
When the heir of a sealed class contains this sealed class as a property, cyclic initialization occurs due to the fact that the SealedClassSerializer in the constructor accesses all passed subclass serializers. Initialization order: Parent Sealed class serializer \/ Create instance of SealedClassSerializer for Parent Sealed \/ Access subclass serializer in constructor of SealedClassSerializer \/ Call ChildClass.Compaion.generatedSerializer() \/ Init ChildClass \/ cache child serializers of ChildClass \/ Create instance of SealedClassSerializer for Parent Sealed ---> /\ Fixes #KT-70516 Merge-request: KT-MR-17421 Merged-by: Sergei Shanshin <[email protected]>
1 parent a79c4ba commit 800b8b5

File tree

2 files changed

+27
-3
lines changed

2 files changed

+27
-3
lines changed

plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -464,13 +464,15 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
464464
serializableProperties: List<IrSerializableProperty>
465465
): List<IrExpression?> {
466466
return DeclarationIrBuilder(compilerContext, symbol).run {
467-
serializableProperties.map { cacheableChildSerializerInstance(serializableClass, it) }
467+
val hasKeepGeneratedSerializerAnnotation = serializableClass.hasKeepGeneratedSerializerAnnotation
468+
serializableProperties.map { cacheableChildSerializerInstance(serializableClass, it, hasKeepGeneratedSerializerAnnotation) }
468469
}
469470
}
470471

471472
private fun IrBuilderWithScope.cacheableChildSerializerInstance(
472473
serializableClass: IrClass,
473-
property: IrSerializableProperty
474+
property: IrSerializableProperty,
475+
hasKeepGeneratedSerializerAnnotation: Boolean
474476
): IrExpression? {
475477
// to avoid a cyclical dependency between the serializer cache and the cache of child serializers,
476478
// the class should not cache its serializer as a child
@@ -486,14 +488,21 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
486488
val serializer = getIrSerialTypeInfo(property, compilerContext).serializer ?: return null
487489
if (serializer.owner.kind == ClassKind.OBJECT) return null
488490

489-
return serializerInstance(
491+
// disable caching for sealed serializer because of initialization loop, see https://github.com/Kotlin/kotlinx.serialization/issues/2759
492+
if (hasKeepGeneratedSerializerAnnotation && serializer.owner.classId == sealedSerializerId) {
493+
return null
494+
}
495+
496+
val serializerInstance = serializerInstance(
490497
serializer,
491498
compilerContext,
492499
property.type,
493500
null,
494501
serializableClass,
495502
null
496503
)
504+
505+
return serializerInstance
497506
}
498507

499508
private fun IrSimpleType.checkTypeArgumentsHasSelf(itselfClass: IrClassSymbol): Boolean {

plugins/kotlinx-serialization/testData/boxIr/KeepGeneratedSerializer.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,19 @@ object ObjectSerializer: KSerializer<Object> {
190190
}
191191
}
192192

193+
// == sealed loop ==
194+
@Serializable
195+
public sealed interface SealedInterface
196+
197+
@Serializable(with = SealedChild.CustomSerializer::class)
198+
@SerialName("child")
199+
@KeepGeneratedSerializer
200+
data class SealedChild(
201+
val child: SealedInterface,
202+
) : SealedInterface {
203+
internal object CustomSerializer : KSerializer<SealedChild> by generatedSerializer()
204+
}
205+
193206
fun box(): String = boxWrapper {
194207
val value = Value(42)
195208
val data = Data(42)
@@ -213,6 +226,8 @@ fun box(): String = boxWrapper {
213226

214227
assertEquals("Object()", Object.generatedSerializer().descriptor.toString(), "Object.generatedSerializer() illegal")
215228
assertSame(Object.generatedSerializer(), Object.generatedSerializer(), "Object.generatedSerializer() instance differs")
229+
230+
assertEquals("SealedInterface", SealedInterface.serializer().descriptor.serialName, "SealedInterface.serializer() illegal")
216231
}
217232

218233
inline fun <reified T : Any> test(

0 commit comments

Comments
 (0)