Auto-applying @Polymorphic for interfaces and serializable abstract

classes
This commit is contained in:
Leonid Startsev
2019-02-20 20:39:33 +03:00
parent 859c95c3c4
commit b108dbe568
6 changed files with 52 additions and 28 deletions
@@ -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)
}
@@ -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? {
@@ -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 {
@@ -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 {
@@ -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,
@@ -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<ClassDescriptor>().singleOrNull()?.defaultType
.getDescriptorsFiltered(nameFilter = { it == SerialEntityNames.SERIALIZER_CLASS_NAME })
.filterIsInstance<ClassDescriptor>().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