Sealed classes serialization

This commit is contained in:
Leonid Startsev
2019-08-22 12:10:52 +03:00
parent 72f8f2b7b5
commit 1ebc65b8e4
9 changed files with 136 additions and 48 deletions
@@ -36,7 +36,7 @@ abstract class SerializableCodegen(
private fun generateSyntheticInternalConstructor() {
val serializerDescriptor = serializableDescriptor.classSerializer ?: return
if (isAbstractSerializableClass(serializableDescriptor) || SerializerCodegen.getSyntheticLoadMember(serializerDescriptor) != null) {
if (serializableDescriptor.isAbstractSerializableClass() || serializableDescriptor.isSealedSerializableClass() || SerializerCodegen.getSyntheticLoadMember(serializerDescriptor) != null) {
val constrDesc = serializableDescriptor.secondaryConstructors.find(ClassConstructorDescriptor::isSerializationCtor) ?: return
generateInternalConstructor(constrDesc)
}
@@ -44,7 +44,7 @@ abstract class SerializableCodegen(
private fun generateSyntheticMethods() {
val serializerDescriptor = serializableDescriptor.classSerializer ?: return
if (isAbstractSerializableClass(serializableDescriptor) || SerializerCodegen.getSyntheticSaveMember(serializerDescriptor) != null) {
if (serializableDescriptor.isAbstractSerializableClass() || serializableDescriptor.isSealedSerializableClass() || SerializerCodegen.getSyntheticSaveMember(serializerDescriptor) != null) {
val func = KSerializerDescriptorResolver.createWriteSelfFunctionDescriptor(serializableDescriptor)
generateWriteSelfMethod(func)
}
@@ -90,6 +90,15 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): S
}
}
fun AbstractSerialGenerator.immediateSealedSerializableSubclassesFor(
klass: ClassDescriptor,
module: ModuleDescriptor
): Pair<List<KotlinType>, List<ClassDescriptor>> {
assert(klass.kind == ClassKind.CLASS && klass.modality == Modality.SEALED)
val serializableSubtypes = klass.sealedSubclasses.map { it.toSimpleType() }
return serializableSubtypes to serializableSubtypes.mapNotNull { findTypeSerializerOrContextUnchecked(module, it) }
}
fun KotlinType.serialName(): String {
val serializableDescriptor = this.toClassDescriptor!!
return serializableDescriptor.serialName()
@@ -27,19 +27,14 @@ import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlinx.serialization.compiler.backend.common.AbstractSerialGenerator
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.jvm.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
@@ -399,11 +394,15 @@ interface IrBuilderExtension {
}
}
fun kClassTypeFor(projection: TypeProjection): SimpleType {
val kClass = compilerContext.builtIns.kClass
return KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, kClass, listOf(projection))
}
fun createClassReference(classType: KotlinType, startOffset: Int, endOffset: Int): IrClassReference {
val clazz = classType.toClassDescriptor!!
val kClass = clazz.module.findClassAcrossModuleDependencies(ClassId(FqName("kotlin.reflect"), Name.identifier("KClass")))!!
val returnType =
KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, kClass, listOf(TypeProjectionImpl(Variance.INVARIANT, classType)))
kClassTypeFor(TypeProjectionImpl(Variance.INVARIANT, classType))
return IrClassReferenceImpl(
startOffset,
endOffset,
@@ -494,12 +493,12 @@ interface IrBuilderExtension {
}
fun wrapIrTypeIntoKSerializerIrType(module: ModuleDescriptor, type: IrType): IrType {
fun wrapIrTypeIntoKSerializerIrType(module: ModuleDescriptor, type: IrType, variance: Variance = Variance.INVARIANT): IrType {
val kSerClass =
compilerContext.externalSymbols.referenceClass(module.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS))
return IrSimpleTypeImpl(
kSerClass, hasQuestionMark = false, arguments = listOf(
makeTypeProjection(type, Variance.INVARIANT)
makeTypeProjection(type, variance)
), annotations = emptyList()
)
}
@@ -542,17 +541,53 @@ interface IrBuilderExtension {
if (serializerClassOriginal.kind == ClassKind.OBJECT) {
return irGetObject(serializerClassOriginal)
} else {
fun instantiate(serializer: ClassDescriptor?, type: KotlinType): IrExpression? {
val expr = serializerInstance(
enclosingGenerator,
serializableDescriptor,
serializer,
module,
type,
type.genericIndex,
genericGetter
) ?: return null
return wrapWithNullableSerializerIfNeeded(module, type, expr, nullableSerClass)
}
var serializerClass = serializerClassOriginal
var args: List<IrExpression>
var typeArgs: List<IrType?>
val thisIrType = kType.toIrType()
when (serializerClassOriginal.classId) {
contextSerializerId, polymorphicSerializerId -> {
args = listOf(classReference(kType))
typeArgs = listOf(kType.toIrType())
typeArgs = listOf(thisIrType)
}
objectSerializerId -> {
args = listOf(irString(kType.serialName()), irGetObject(kType.toClassDescriptor!!))
typeArgs = listOf(kType.toIrType())
typeArgs = listOf(thisIrType)
}
sealedSerializerId -> {
args = mutableListOf<IrExpression>().apply {
add(irString(kType.serialName()))
val (subclasses, subSerializers) = enclosingGenerator.immediateSealedSerializableSubclassesFor(
kType.toClassDescriptor!!,
module
)
val projectedOutCurrentKClass = kClassTypeFor(TypeProjectionImpl(Variance.OUT_VARIANCE, kType))
add(
createArrayOfExpression(
projectedOutCurrentKClass.toIrType(),
subclasses.map { classReference(it) }
)
)
add(
createArrayOfExpression(
wrapIrTypeIntoKSerializerIrType(module, thisIrType, variance = Variance.OUT_VARIANCE),
subSerializers.mapIndexed { i, ser -> instantiate(ser, subclasses[i])!! }
)
)
}
typeArgs = listOf(thisIrType)
}
enumSerializerId -> {
serializerClass = serializableDescriptor.getClassFromInternalSerializationPackage("CommonEnumSerializer")
@@ -566,7 +601,7 @@ interface IrBuilderExtension {
)
)
}
typeArgs = listOf(kType.toIrType())
typeArgs = listOf(thisIrType)
}
else -> {
args = kType.arguments.map {
@@ -575,17 +610,7 @@ interface IrBuilderExtension {
it.type,
sourceElement = serializerClassOriginal.findPsi()
)
val expr = serializerInstance(
enclosingGenerator,
serializableDescriptor,
argSer,
module,
it.type,
it.type.genericIndex,
genericGetter
)
?: return null
wrapWithNullableSerializerIfNeeded(module, it.type, expr, nullableSerClass)
instantiate(argSer, it.type) ?: return null
}
typeArgs = kType.arguments.map { it.type.toIrType() }
}
@@ -606,8 +631,7 @@ interface IrBuilderExtension {
} else {
compilerContext.externalSymbols.referenceConstructor(serializerClass.unsubstitutedPrimaryConstructor!!)
}
val propertyTypeHint = kType.toIrType()
val returnType = wrapIrTypeIntoKSerializerIrType(module, propertyTypeHint)
val returnType = wrapIrTypeIntoKSerializerIrType(module, thisIrType)
return irInvoke(null, ctor, typeArguments = typeArgs, valueArguments = args, returnTypeHint = returnType)
}
}
@@ -145,6 +145,10 @@ internal fun AbstractSerialGenerator.serializerInstance(
if (serializerClass.kind == ClassKind.OBJECT) {
return context.serializerObjectGetter(serializerClass)
} else {
fun instantiate(serializer: ClassDescriptor?, type: KotlinType): JsExpression? {
val expr = serializerInstance(context, serializer, module, type, type.genericIndex, genericGetter) ?: return null
return if (type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr
}
var args = when {
serializerClass.classId == contextSerializerId || serializerClass.classId == polymorphicSerializerId -> listOf(
ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)
@@ -157,10 +161,25 @@ internal fun AbstractSerialGenerator.serializerInstance(
JsStringLiteral(kType.serialName()),
context.serializerObjectGetter(kType.toClassDescriptor!!)
)
serializerClass.classId == sealedSerializerId -> mutableListOf<JsExpression>().apply {
add(JsStringLiteral(kType.serialName()))
val (subclasses, subSerializers) = immediateSealedSerializableSubclassesFor(
kType.toClassDescriptor!!,
module
)
add(JsArrayLiteral(subclasses.map {
ExpressionVisitor.getObjectKClass(
context,
it.toClassDescriptor!!
)
}))
add(JsArrayLiteral(subSerializers.mapIndexed { i, ser ->
instantiate(ser, subclasses[i])!!
}))
}
else -> kType.arguments.map {
val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi())
val expr = serializerInstance(context, argSer, module, it.type, it.type.genericIndex, genericGetter) ?: return null
if (it.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr
instantiate(argSer, it.type) ?: return null
}
}
if (serializerClass.classId == referenceArraySerializerId)
@@ -159,6 +159,7 @@ internal val enumSerializerId = ClassId(internalPackageFqName, Name.identifier(S
internal val polymorphicSerializerId = ClassId(packageFqName, Name.identifier(SpecialBuiltins.polymorphicSerializer))
internal val referenceArraySerializerId = ClassId(internalPackageFqName, Name.identifier(SpecialBuiltins.referenceArraySerializer))
internal val objectSerializerId = ClassId(internalPackageFqName, Name.identifier(SpecialBuiltins.objectSerializer))
internal val sealedSerializerId = ClassId(packageFqName, Name.identifier(SpecialBuiltins.sealedSerializer))
internal val contextSerializerId = ClassId(packageFqName, Name.identifier(SpecialBuiltins.contextSerializer))
@@ -263,7 +264,7 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
// instantiate all arg serializers on stack
val signature = StringBuilder("(")
fun instantiate(typeArgument: Pair<KotlinType, ClassDescriptor?>) {
fun instantiate(typeArgument: Pair<KotlinType, ClassDescriptor?>, writeSignature: Boolean = true) {
val (argType, argSerializer) = typeArgument
assert(
stackValueSerializerInstance(
@@ -278,9 +279,10 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
)
// wrap into nullable serializer if argType is nullable
if (argType.isMarkedNullable) wrapStackValueIntoNullableSerializer()
signature.append(kSerializerType.descriptor)
if (writeSignature) signature.append(kSerializerType.descriptor)
}
val serialName = kType.serialName()
when (serializer.classId) {
enumSerializerId, contextSerializerId, polymorphicSerializerId -> {
// a special way to instantiate enum -- need a enum KClass reference
@@ -297,15 +299,30 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
// Reference array serializer still needs serializer for its argument type
instantiate(argSerializers[0])
}
sealedSerializerId -> {
aconst(serialName)
signature.append("Ljava/lang/String;")
val (subClasses, subSerializers) = immediateSealedSerializableSubclassesFor(kType.toClassDescriptor!!, module)
// KClasses vararg
fillArray(AsmTypes.K_CLASS_TYPE, subClasses) { i, type ->
aconst(codegen.typeMapper.mapType(type, null, TypeMappingMode.GENERIC_ARGUMENT))
AsmUtil.wrapJavaClassIntoKClass(this)
}
signature.append(AsmTypes.K_CLASS_ARRAY_TYPE.descriptor)
// Serializers vararg
fillArray(kSerializerType, subSerializers) { i, serializer ->
instantiate(subClasses[i] to serializer, writeSignature = false)
}
signature.append(kSerializerArrayType.descriptor)
}
objectSerializerId -> {
val serialName = kType.serialName()
aconst(serialName)
signature.append("Ljava/lang/String;")
StackValue.singleton(kType.toClassDescriptor!!, codegen.typeMapper).put(Type.getType("Ljava/lang/Object;"), iv)
signature.append("Ljava/lang/Object;")
}
// all serializers get arguments with serializers of their generic types
else -> argSerializers.forEach(::instantiate)
else -> argSerializers.forEach { instantiate(it) }
}
signature.append(")V")
// invoke constructor
@@ -320,6 +337,17 @@ fun InstructionAdapter.wrapStackValueIntoNullableSerializer() =
"(" + kSerializerType.descriptor + ")" + kSerializerType.descriptor, false
)
fun <T> InstructionAdapter.fillArray(type: Type, args: List<T>, onEach: (Int, T) -> Unit) {
iconst(args.size)
newarray(type)
args.forEachIndexed { i, serializer ->
dup()
iconst(i)
onEach(i, serializer)
astore(type)
}
}
//
// ======= Serializers Resolving =======
//
@@ -28,9 +28,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.*
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_CLASS
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName
internal fun isAllowedToHaveAutoGeneratedSerializerMethods(
@@ -128,7 +127,8 @@ internal fun ClassDescriptor.isSerializableEnum(): Boolean {
return false
}
internal val ClassDescriptor.shouldHaveGeneratedSerializer: Boolean get() = isInternalSerializable || isSerializableEnum()
internal val ClassDescriptor.shouldHaveGeneratedSerializer: Boolean
get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isSerializableEnum()
internal fun ClassDescriptor.enumEntries(): List<ClassDescriptor> {
check(this.kind == ClassKind.ENUM_CLASS)
@@ -158,14 +158,21 @@ internal fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntr
// For abstract classes marked with @Serializable,
// methods are generated anyway although they shouldn't have
// generated $serializer and use Polymorphic one.
internal fun isAbstractSerializableClass(serializableDescriptor: ClassDescriptor): Boolean =
serializableDescriptor.isInternalSerializable && serializableDescriptor.modality == Modality.ABSTRACT
internal fun ClassDescriptor.isAbstractSerializableClass(): Boolean =
isInternalSerializable && modality == Modality.ABSTRACT
internal fun ClassDescriptor.polymorphicSerializerIfApplicableAutomatically(): ClassDescriptor? = if (
isAbstractSerializableClass(this)
|| kind == ClassKind.INTERFACE
) module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer)
else null
internal fun ClassDescriptor.isSealedSerializableClass(): Boolean =
isInternalSerializable && modality == Modality.SEALED
internal fun ClassDescriptor.polymorphicSerializerIfApplicableAutomatically(): ClassDescriptor? {
val serializer = when {
this.isAbstractSerializableClass()
|| kind == ClassKind.INTERFACE -> SpecialBuiltins.polymorphicSerializer
this.isSealedSerializableClass() -> SpecialBuiltins.sealedSerializer
else -> null
}
return serializer?.let { module.getClassFromSerializationPackage(it) }
}
// serializer that was declared for this type
internal val ClassDescriptor?.classSerializer: ClassDescriptor?
@@ -291,7 +291,7 @@ object KSerializerDescriptorResolver {
)
val markerDesc = classDescriptor.getKSerializerConstructorMarker()
val markerType = markerDesc.toSimpleType()
val markerType = markerDesc.toSimpleType(nullable = true)
val serializableProperties = bindingContext.serializablePropertiesFor(classDescriptor).serializableProperties
val parameterDescsAsProps = serializableProperties.map { it.descriptor }
@@ -80,6 +80,7 @@ object SpecialBuiltins {
const val objectSerializer = "ObjectSerializer"
const val enumSerializer = "EnumSerializer"
const val polymorphicSerializer = "PolymorphicSerializer"
const val sealedSerializer = "SealedClassSerializer"
const val contextSerializer = "ContextSerializer"
const val nullableSerializer = "NullableSerializer"
}
@@ -91,7 +91,7 @@ internal fun ClassDescriptor.getClassFromSerializationPackage(classSimpleName: S
internal fun ClassDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
module.getClassFromInternalSerializationPackage(classSimpleName)
fun ClassDescriptor.toSimpleType(nullable: Boolean = true) =
fun ClassDescriptor.toSimpleType(nullable: Boolean = false) =
KotlinTypeFactory.simpleType(Annotations.EMPTY, this.typeConstructor, emptyList(), nullable)
internal fun Annotated.annotationsWithArguments(): List<Triple<ClassDescriptor, List<ValueArgument>, List<ValueParameterDescriptor>>> =