diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt index f6f77a32bb3..d6334ea2ccb 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties import org.jetbrains.kotlinx.serialization.compiler.resolve.classSerializer -import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor +import org.jetbrains.kotlinx.serialization.compiler.resolve.isAbstractSerializableClass abstract class SerializableCodegen( protected val serializableDescriptor: ClassDescriptor, @@ -37,16 +37,16 @@ abstract class SerializableCodegen( } private fun generateSyntheticInternalConstructor() { - val serializerDescriptor = serializableDescriptor.classSerializer?.toClassDescriptor ?: return - if (SerializerCodegen.getSyntheticLoadMember(serializerDescriptor) != null) { + val serializerDescriptor = serializableDescriptor.classSerializer ?: return + if (isAbstractSerializableClass(serializableDescriptor) || SerializerCodegen.getSyntheticLoadMember(serializerDescriptor) != null) { val constrDesc = KSerializerDescriptorResolver.createLoadConstructorDescriptor(serializableDescriptor, bindingContext) generateInternalConstructor(constrDesc) } } private fun generateSyntheticMethods() { - val serializerDescriptor = serializableDescriptor.classSerializer?.toClassDescriptor ?: return - if (SerializerCodegen.getSyntheticSaveMember(serializerDescriptor) != null) { + val serializerDescriptor = serializableDescriptor.classSerializer ?: return + if (isAbstractSerializableClass(serializableDescriptor) || SerializerCodegen.getSyntheticSaveMember(serializerDescriptor) != null) { val func = KSerializerDescriptorResolver.createWriteSelfFunctionDescriptor(serializableDescriptor) generateWriteSelfMethod(func) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt index 81627387ddd..868f8721e0c 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt @@ -30,11 +30,9 @@ import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.* import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId -import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.internalPackageFqName @@ -130,9 +128,11 @@ fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescri if (userOverride != null) return userOverride.toClassDescriptor if (kType.isTypeParameter()) return null if (KotlinBuiltIns.isArray(kType)) return module.getClassFromInternalSerializationPackage(SpecialBuiltins.referenceArraySerializer) - return kType.typeSerializer.toClassDescriptor // check for serializer defined on the type - ?: findStandardKotlinTypeSerializer(module, kType) // otherwise see if there is a standard serializer - ?: findEnumTypeSerializer(module, kType) + val stdSer = findStandardKotlinTypeSerializer(module, kType) // see if there is a standard serializer + ?: findEnumTypeSerializer(module, kType) + if (stdSer != null) return stdSer + if (kType.isInterface()) return module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer) + return kType.toClassDescriptor?.classSerializer // check for serializer defined on the type } fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt index f32e776b3e8..6894dddc89c 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt @@ -12,7 +12,10 @@ import org.jetbrains.kotlin.ir.util.TypeTranslator import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen -import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlinx.serialization.compiler.resolve.classSerializer +import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion +import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion class SerializableCompanionIrGenerator( val irClass: IrClass, @@ -39,7 +42,7 @@ class SerializableCompanionIrGenerator( override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) = irClass.contributeFunction(methodDescriptor, fromStubs = true) { getter -> - val serializer = serializableDescriptor.classSerializer?.toClassDescriptor!! + val serializer = serializableDescriptor.classSerializer!! val expr = if (serializer.kind == ClassKind.OBJECT) { irGetObject(serializer) } else { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt index 692431b7035..04ff8d86a3f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt @@ -27,7 +27,10 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen -import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlinx.serialization.compiler.resolve.classSerializer +import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion +import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion class SerializableCompanionJsTranslator( declaration: ClassDescriptor, @@ -37,7 +40,7 @@ class SerializableCompanionJsTranslator( override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) { val f = context.buildFunction(methodDescriptor) {jsFun, context -> - val serializer = serializableDescriptor.classSerializer?.toClassDescriptor!! + val serializer = serializableDescriptor.classSerializer!! val stmt: JsExpression = if (serializer.kind == ClassKind.OBJECT) { context.serializerObjectGetter(serializer) } else { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt index ab73ff9bdfc..ae7a1018854 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt @@ -20,7 +20,9 @@ import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen -import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.classSerializer +import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion +import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationBodyCodegen) : SerializableCompanionCodegen(classCodegen.descriptor, classCodegen.bindingContext) { @@ -34,7 +36,7 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB } override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) { - val serial = serializableDescriptor.classSerializer?.toClassDescriptor ?: return + val serial = serializableDescriptor.classSerializer ?: return classCodegen.generateMethod(methodDescriptor) { _, _ -> stackValueSerializerInstance( classCodegen, diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt index 1800942a742..4c0c9f24713 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt @@ -117,19 +117,33 @@ internal val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean return psi.valueArguments.isEmpty() } +// 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.polymorphicSerializerIfApplicableAutomatically(): ClassDescriptor? = if ( + isAbstractSerializableClass(this) + || kind == ClassKind.INTERFACE +) module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer) +else null + // serializer that was declared for this type -internal val ClassDescriptor?.classSerializer: KotlinType? +internal val ClassDescriptor?.classSerializer: ClassDescriptor? get() = this?.let { // serializer annotation on class? - serializableWith?.let { return it } + serializableWith?.let { return it.toClassDescriptor } // companion object serializer? - if (hasCompanionObjectAsSerializer) return companionObjectDescriptor?.defaultType + if (hasCompanionObjectAsSerializer) return companionObjectDescriptor + // can infer @Poly? + polymorphicSerializerIfApplicableAutomatically()?.let { return it } // default serializable? if (isInternalSerializable) { // $serializer nested class return this.unsubstitutedMemberScope - .getDescriptorsFiltered(nameFilter = {it == SerialEntityNames.SERIALIZER_CLASS_NAME}) - .filterIsInstance().singleOrNull()?.defaultType + .getDescriptorsFiltered(nameFilter = { it == SerialEntityNames.SERIALIZER_CLASS_NAME }) + .filterIsInstance().singleOrNull() } return null } @@ -139,8 +153,8 @@ internal val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean internal fun checkSerializerNullability(classType: KotlinType, serializerType: KotlinType): KotlinType { val castedToKSerial = requireNotNull( - serializerType.supertypes().find { isKSerializer(it) }, - { "${KSERIALIZER_CLASS} is not a supertype of $serializerType" } + serializerType.supertypes().find { isKSerializer(it) }, + { "${KSERIALIZER_CLASS} is not a supertype of $serializerType" } ) if (!classType.isMarkedNullable && castedToKSerial.arguments.first().type.isMarkedNullable) throw IllegalStateException("Can't serialize non-nullable field of type ${classType} with nullable serializer ${serializerType}") @@ -149,11 +163,13 @@ internal fun checkSerializerNullability(classType: KotlinType, serializerType: K // returns only user-overriden Serializer val KotlinType.overridenSerializer: KotlinType? - get() = (this.toClassDescriptor?.serializableWith)?.let { checkSerializerNullability(this, it) } - -// serializer that was declared for this specific type or annotation from a class declaration -val KotlinType.typeSerializer: KotlinType? - get() = this.toClassDescriptor?.classSerializer + get() { + val desc = this.toClassDescriptor ?: return null + (desc.serializableWith)?.let { return checkSerializerNullability(this, it) } + if (desc.annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName)) + return desc.module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer).defaultType + return null + } val KotlinType.genericIndex: Int? get() = (this.constructor.declarationDescriptor as? TypeParameterDescriptor)?.index