Support sealed, generic and abstract polymorphic inheritors in sealed class serialization

This commit is contained in:
Leonid Startsev
2019-08-28 14:29:26 +03:00
parent 1ebc65b8e4
commit 855a867893
9 changed files with 97 additions and 39 deletions
@@ -90,12 +90,16 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): S
} }
} }
fun AbstractSerialGenerator.immediateSealedSerializableSubclassesFor( fun AbstractSerialGenerator.allSealedSerializableSubclassesFor(
klass: ClassDescriptor, klass: ClassDescriptor,
module: ModuleDescriptor module: ModuleDescriptor
): Pair<List<KotlinType>, List<ClassDescriptor>> { ): Pair<List<KotlinType>, List<ClassDescriptor>> {
assert(klass.kind == ClassKind.CLASS && klass.modality == Modality.SEALED) assert(klass.kind == ClassKind.CLASS && klass.modality == Modality.SEALED)
val serializableSubtypes = klass.sealedSubclasses.map { it.toSimpleType() } fun recursiveSealed(klass: ClassDescriptor): Collection<ClassDescriptor> {
return klass.sealedSubclasses.flatMap { if (it.modality == Modality.SEALED) recursiveSealed(it) else setOf(it) }
}
val serializableSubtypes = recursiveSealed(klass).map { it.toSimpleType() }
return serializableSubtypes to serializableSubtypes.mapNotNull { findTypeSerializerOrContextUnchecked(module, it) } return serializableSubtypes to serializableSubtypes.mapNotNull { findTypeSerializerOrContextUnchecked(module, it) }
} }
@@ -33,8 +33,8 @@ import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlinx.serialization.compiler.backend.common.AbstractSerialGenerator import org.jetbrains.kotlinx.serialization.compiler.backend.common.AbstractSerialGenerator
import org.jetbrains.kotlinx.serialization.compiler.backend.common.allSealedSerializableSubclassesFor
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext
import org.jetbrains.kotlinx.serialization.compiler.backend.common.immediateSealedSerializableSubclassesFor
import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialName import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialName
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.* import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
@@ -466,7 +466,6 @@ interface IrBuilderExtension {
return serializerInstance( return serializerInstance(
generator, generator,
dispatchReceiverParameter, dispatchReceiverParameter,
generator.serializableDescriptor,
serializer, serializer,
property.module, property.module,
property.type, property.type,
@@ -506,37 +505,34 @@ interface IrBuilderExtension {
fun IrBuilderWithScope.serializerInstance( fun IrBuilderWithScope.serializerInstance(
enclosingGenerator: SerializerIrGenerator, enclosingGenerator: SerializerIrGenerator,
dispatchReceiverParameter: IrValueParameter, dispatchReceiverParameter: IrValueParameter,
serializableDescriptor: ClassDescriptor,
serializerClassOriginal: ClassDescriptor?, serializerClassOriginal: ClassDescriptor?,
module: ModuleDescriptor, module: ModuleDescriptor,
kType: KotlinType, kType: KotlinType,
genericIndex: Int? = null genericIndex: Int? = null
): IrExpression? = serializerInstance( ): IrExpression? = serializerInstance(
enclosingGenerator, enclosingGenerator,
serializableDescriptor,
serializerClassOriginal, serializerClassOriginal,
module, module,
kType, kType,
genericIndex genericIndex
) { ) { it, _ ->
val prop = enclosingGenerator.localSerializersFieldsDescriptors[it] val prop = enclosingGenerator.localSerializersFieldsDescriptors[it]
irGetField(irGet(dispatchReceiverParameter), compilerContext.localSymbolTable.referenceField(prop).owner) irGetField(irGet(dispatchReceiverParameter), compilerContext.localSymbolTable.referenceField(prop).owner)
} }
fun IrBuilderWithScope.serializerInstance( fun IrBuilderWithScope.serializerInstance(
enclosingGenerator: AbstractSerialGenerator, enclosingGenerator: AbstractSerialGenerator,
serializableDescriptor: ClassDescriptor,
serializerClassOriginal: ClassDescriptor?, serializerClassOriginal: ClassDescriptor?,
module: ModuleDescriptor, module: ModuleDescriptor,
kType: KotlinType, kType: KotlinType,
genericIndex: Int? = null, genericIndex: Int? = null,
genericGetter: (Int) -> IrExpression genericGetter: ((Int, KotlinType) -> IrExpression)? = null
): IrExpression? { ): IrExpression? {
val nullableSerClass = val nullableSerClass =
compilerContext.externalSymbols.referenceClass(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer)) compilerContext.externalSymbols.referenceClass(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer))
if (serializerClassOriginal == null) { if (serializerClassOriginal == null) {
if (genericIndex == null) return null if (genericIndex == null) return null
return genericGetter(genericIndex) return genericGetter?.invoke(genericIndex, kType)
} }
if (serializerClassOriginal.kind == ClassKind.OBJECT) { if (serializerClassOriginal.kind == ClassKind.OBJECT) {
return irGetObject(serializerClassOriginal) return irGetObject(serializerClassOriginal)
@@ -544,7 +540,6 @@ interface IrBuilderExtension {
fun instantiate(serializer: ClassDescriptor?, type: KotlinType): IrExpression? { fun instantiate(serializer: ClassDescriptor?, type: KotlinType): IrExpression? {
val expr = serializerInstance( val expr = serializerInstance(
enclosingGenerator, enclosingGenerator,
serializableDescriptor,
serializer, serializer,
module, module,
type, type,
@@ -569,7 +564,8 @@ interface IrBuilderExtension {
sealedSerializerId -> { sealedSerializerId -> {
args = mutableListOf<IrExpression>().apply { args = mutableListOf<IrExpression>().apply {
add(irString(kType.serialName())) add(irString(kType.serialName()))
val (subclasses, subSerializers) = enclosingGenerator.immediateSealedSerializableSubclassesFor( add(classReference(kType))
val (subclasses, subSerializers) = enclosingGenerator.allSealedSerializableSubclassesFor(
kType.toClassDescriptor!!, kType.toClassDescriptor!!,
module module
) )
@@ -583,14 +579,33 @@ interface IrBuilderExtension {
add( add(
createArrayOfExpression( createArrayOfExpression(
wrapIrTypeIntoKSerializerIrType(module, thisIrType, variance = Variance.OUT_VARIANCE), wrapIrTypeIntoKSerializerIrType(module, thisIrType, variance = Variance.OUT_VARIANCE),
subSerializers.mapIndexed { i, ser -> instantiate(ser, subclasses[i])!! } subSerializers.mapIndexed { i, serializer ->
val type = subclasses[i]
val expr = serializerInstance(
enclosingGenerator,
serializer,
module,
type,
type.genericIndex
) { _, genericType ->
serializerInstance(
enclosingGenerator,
module.getClassFromSerializationPackage(
SpecialBuiltins.polymorphicSerializer
),
module,
genericType
)!!
}!!
wrapWithNullableSerializerIfNeeded(module, type, expr, nullableSerClass)
}
) )
) )
} }
typeArgs = listOf(thisIrType) typeArgs = listOf(thisIrType)
} }
enumSerializerId -> { enumSerializerId -> {
serializerClass = serializableDescriptor.getClassFromInternalSerializationPackage("CommonEnumSerializer") serializerClass = module.getClassFromInternalSerializationPackage("CommonEnumSerializer")
args = kType.toClassDescriptor!!.let { enumDesc -> args = kType.toClassDescriptor!!.let { enumDesc ->
listOf( listOf(
irString(enumDesc.serialName()), irString(enumDesc.serialName()),
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.TypeTranslator import org.jetbrains.kotlin.ir.util.TypeTranslator
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.defaultType
@@ -23,8 +22,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion
import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion
class SerializableCompanionIrGenerator( class SerializableCompanionIrGenerator(
val irClass: IrClass, val irClass: IrClass,
@@ -94,10 +91,9 @@ class SerializableCompanionIrGenerator(
val args: List<IrExpression> = getter.valueParameters.map { irGet(it) } val args: List<IrExpression> = getter.valueParameters.map { irGet(it) }
val expr = serializerInstance( val expr = serializerInstance(
this@SerializableCompanionIrGenerator, this@SerializableCompanionIrGenerator,
serializableDescriptor, serializer, serializer, serializableDescriptor.module,
serializableDescriptor.module,
serializableDescriptor.defaultType serializableDescriptor.defaultType
) { args[it] } ) { it, _ -> args[it] }
patchSerializableClassWithMarkerAnnotation(serializer) patchSerializableClassWithMarkerAnnotation(serializer)
+irReturn(requireNotNull(expr)) +irReturn(requireNotNull(expr))
} }
@@ -248,7 +248,6 @@ open class SerializerIrGenerator(val irClass: IrClass, final override val compil
val innerSerial = serializerInstance( val innerSerial = serializerInstance(
this@SerializerIrGenerator, this@SerializerIrGenerator,
saveFunc.dispatchReceiverParameter!!, saveFunc.dispatchReceiverParameter!!,
serializableDescriptor,
sti.serializer, sti.serializer,
property.module, property.module,
property.type, property.type,
@@ -381,7 +380,6 @@ open class SerializerIrGenerator(val irClass: IrClass, final override val compil
val innerSerial = serializerInstance( val innerSerial = serializerInstance(
this@SerializerIrGenerator, this@SerializerIrGenerator,
loadFunc.dispatchReceiverParameter!!, loadFunc.dispatchReceiverParameter!!,
serializableDescriptor,
sti.serializer, sti.serializer,
property.module, property.module,
property.type, property.type,
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
import org.jetbrains.kotlinx.serialization.compiler.backend.common.* import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.* import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
@@ -134,13 +135,18 @@ internal fun AbstractSerialGenerator.serializerInstance(
module: ModuleDescriptor, module: ModuleDescriptor,
kType: KotlinType, kType: KotlinType,
genericIndex: Int? = null, genericIndex: Int? = null,
genericGetter: (Int) -> JsExpression = { JsNameRef(context.scope().declareName("${SerialEntityNames.typeArgPrefix}$it"), JsThisRef()) } genericGetter: (Int, KotlinType) -> JsExpression = { it, _ ->
JsNameRef(
context.scope().declareName("${SerialEntityNames.typeArgPrefix}$it"),
JsThisRef()
)
}
): JsExpression? { ): JsExpression? {
val nullableSerClass = val nullableSerClass =
context.translateQualifiedReference(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer)) context.translateQualifiedReference(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer))
if (serializerClass == null) { if (serializerClass == null) {
if (genericIndex == null) return null if (genericIndex == null) return null
return genericGetter(genericIndex) return genericGetter(genericIndex, kType)
} }
if (serializerClass.kind == ClassKind.OBJECT) { if (serializerClass.kind == ClassKind.OBJECT) {
return context.serializerObjectGetter(serializerClass) return context.serializerObjectGetter(serializerClass)
@@ -163,7 +169,8 @@ internal fun AbstractSerialGenerator.serializerInstance(
) )
serializerClass.classId == sealedSerializerId -> mutableListOf<JsExpression>().apply { serializerClass.classId == sealedSerializerId -> mutableListOf<JsExpression>().apply {
add(JsStringLiteral(kType.serialName())) add(JsStringLiteral(kType.serialName()))
val (subclasses, subSerializers) = immediateSealedSerializableSubclassesFor( add(ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!))
val (subclasses, subSerializers) = allSealedSerializableSubclassesFor(
kType.toClassDescriptor!!, kType.toClassDescriptor!!,
module module
) )
@@ -173,8 +180,17 @@ internal fun AbstractSerialGenerator.serializerInstance(
it.toClassDescriptor!! it.toClassDescriptor!!
) )
})) }))
add(JsArrayLiteral(subSerializers.mapIndexed { i, ser -> add(JsArrayLiteral(subSerializers.mapIndexed { i, serializer ->
instantiate(ser, subclasses[i])!! val type = subclasses[i]
val expr = serializerInstance(context, serializer, module, type, type.genericIndex) { _, genericType ->
serializerInstance(
context,
module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer),
module,
(genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound
)!!
}!!
if (type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr
})) }))
} }
else -> kType.arguments.map { else -> kType.arguments.map {
@@ -52,7 +52,7 @@ class SerializableCompanionJsTranslator(
serializer, serializer,
serializableDescriptor.module, serializableDescriptor.module,
serializableDescriptor.defaultType, serializableDescriptor.defaultType,
genericGetter = { genericGetter = { it, _ ->
args[it] args[it]
}) })
) )
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
import org.jetbrains.kotlinx.serialization.compiler.backend.common.* import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS
@@ -177,7 +178,7 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromClass(
serializer, serializer,
this, this,
sti.property.genericIndex sti.property.genericIndex
) { idx -> ) { idx, _ ->
load(varIndexStart + idx, kSerializerType) load(varIndexStart + idx, kSerializerType)
} }
} }
@@ -201,7 +202,7 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializerWithou
serializer, serializer,
this, this,
property.genericIndex property.genericIndex
) { idx -> ) { idx, _ ->
load(0, kSerializerType) load(0, kSerializerType)
getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$idx", kSerializerType.descriptor) getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$idx", kSerializerType.descriptor)
}.also { if (it && property.type.isMarkedNullable) wrapStackValueIntoNullableSerializer() } }.also { if (it && property.type.isMarkedNullable) wrapStackValueIntoNullableSerializer() }
@@ -211,7 +212,7 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(codeg
return serializerCodegen.stackValueSerializerInstance( return serializerCodegen.stackValueSerializerInstance(
codegen, sti.property.module, sti.property.type, codegen, sti.property.module, sti.property.type,
sti.serializer, this, sti.property.genericIndex sti.serializer, this, sti.property.genericIndex
) { idx -> ) { idx, _ ->
load(0, kSerializerType) load(0, kSerializerType)
getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$idx", kSerializerType.descriptor) getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$idx", kSerializerType.descriptor)
} }
@@ -220,10 +221,13 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(codeg
// returns false is cannot not use serializer // returns false is cannot not use serializer
// use iv == null to check only (do not emit serializer onto stack) // use iv == null to check only (do not emit serializer onto stack)
internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?, internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?,
iv: InstructionAdapter?, genericIndex: Int? = null, genericSerializerFieldGetter: (InstructionAdapter.(Int) -> Unit)? = null): Boolean { iv: InstructionAdapter?,
genericIndex: Int? = null,
genericSerializerFieldGetter: (InstructionAdapter.(Int, KotlinType) -> Unit)? = null
): Boolean {
if (maybeSerializer == null && genericIndex != null) { if (maybeSerializer == null && genericIndex != null) {
// get field from serializer object // get field from serializer object
iv?.run { genericSerializerFieldGetter?.invoke(this, genericIndex) } iv?.run { genericSerializerFieldGetter?.invoke(this, genericIndex, kType) }
return true return true
} }
val serializer = maybeSerializer ?: return false val serializer = maybeSerializer ?: return false
@@ -302,16 +306,41 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
sealedSerializerId -> { sealedSerializerId -> {
aconst(serialName) aconst(serialName)
signature.append("Ljava/lang/String;") signature.append("Ljava/lang/String;")
val (subClasses, subSerializers) = immediateSealedSerializableSubclassesFor(kType.toClassDescriptor!!, module) aconst(codegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT))
AsmUtil.wrapJavaClassIntoKClass(this)
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
val (subClasses, subSerializers) = allSealedSerializableSubclassesFor(kType.toClassDescriptor!!, module)
// KClasses vararg // KClasses vararg
fillArray(AsmTypes.K_CLASS_TYPE, subClasses) { i, type -> fillArray(AsmTypes.K_CLASS_TYPE, subClasses) { _, type ->
aconst(codegen.typeMapper.mapType(type, null, TypeMappingMode.GENERIC_ARGUMENT)) aconst(codegen.typeMapper.mapType(type, null, TypeMappingMode.GENERIC_ARGUMENT))
AsmUtil.wrapJavaClassIntoKClass(this) AsmUtil.wrapJavaClassIntoKClass(this)
} }
signature.append(AsmTypes.K_CLASS_ARRAY_TYPE.descriptor) signature.append(AsmTypes.K_CLASS_ARRAY_TYPE.descriptor)
// Serializers vararg // Serializers vararg
fillArray(kSerializerType, subSerializers) { i, serializer -> fillArray(kSerializerType, subSerializers) { i, serializer ->
instantiate(subClasses[i] to serializer, writeSignature = false) val (argType, argSerializer) = subClasses[i] to serializer
assert(
stackValueSerializerInstance(
codegen,
module,
argType,
argSerializer,
this,
argType.genericIndex
) { _, genericType ->
// if we encountered generic type parameter in one of subclasses of sealed class, use polymorphism from upper bound
assert(
stackValueSerializerInstance(
codegen,
module,
(genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound,
module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer),
this
)
)
}
)
if (argType.isMarkedNullable) wrapStackValueIntoNullableSerializer()
} }
signature.append(kSerializerArrayType.descriptor) signature.append(kSerializerArrayType.descriptor)
} }
@@ -104,8 +104,8 @@ class SerializableCodegenImpl(
superTypeArguments.forEach { superTypeArguments.forEach {
val genericIdx = serializableDescriptor.defaultType.arguments.indexOf(it).let { if (it == -1) null else it } val genericIdx = serializableDescriptor.defaultType.arguments.indexOf(it).let { if (it == -1) null else it }
val serial = findTypeSerializerOrContext(serializableDescriptor.module, it.type) val serial = findTypeSerializerOrContext(serializableDescriptor.module, it.type)
stackValueSerializerInstance(classCodegen, serializableDescriptor.module, it.type, serial, this, genericIdx) { stackValueSerializerInstance(classCodegen, serializableDescriptor.module, it.type, serial, this, genericIdx) { i, _ ->
load(offsetI + it, kSerializerType) load(offsetI + i, kSerializerType)
} }
} }
val superSignature = val superSignature =
@@ -51,7 +51,7 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB
serial, serial,
this, this,
null null
) { ) { it, _ ->
load(it + 1, kSerializerType) load(it + 1, kSerializerType)
} }
areturn(kSerializerType) areturn(kSerializerType)