Instantiating of Polymorphic in Companion.serializer() for JS and Native

This commit is contained in:
Leonid Startsev
2019-02-21 14:39:33 +03:00
parent b108dbe568
commit a2465910a3
4 changed files with 74 additions and 65 deletions
@@ -12,10 +12,7 @@ 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.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
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
class SerializableCompanionIrGenerator(
val irClass: IrClass,
@@ -43,15 +40,25 @@ class SerializableCompanionIrGenerator(
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) =
irClass.contributeFunction(methodDescriptor, fromStubs = true) { getter ->
val serializer = serializableDescriptor.classSerializer!!
val expr = if (serializer.kind == ClassKind.OBJECT) {
irGetObject(serializer)
} else {
val desc = requireNotNull(
KSerializerDescriptorResolver.findSerializerConstructorForTypeArgumentsSerializers(serializer)
) { "Generated serializer does not have constructor with required number of arguments" }
val ctor = compilerContext.externalSymbols.referenceConstructor(desc)
val args: List<IrExpression> = getter.valueParameters.map { irGet(it) }
irInvoke(null, ctor, *args.toTypedArray())
val expr = when {
serializer.kind == ClassKind.OBJECT -> irGetObject(serializer)
serializer.isSerializerWhichRequiersKClass() -> {
val serializableType = serializableDescriptor.defaultType
irInvoke(
null,
compilerContext.externalSymbols.referenceConstructor(serializer.unsubstitutedPrimaryConstructor!!),
listOf(serializableType.toIrType()),
listOf(classReference(serializableType))
)
}
else -> {
val desc = requireNotNull(
KSerializerDescriptorResolver.findSerializerConstructorForTypeArgumentsSerializers(serializer)
) { "Generated serializer does not have constructor with required number of arguments" }
val ctor = compilerContext.externalSymbols.referenceConstructor(desc)
val args: List<IrExpression> = getter.valueParameters.map { irGet(it) }
irInvoke(null, ctor, *args.toTypedArray())
}
}
+irReturn(expr)
}
@@ -33,9 +33,6 @@ import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext
import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryPropertiesDescriptorsMap
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId
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.*
@@ -134,8 +131,6 @@ internal fun SerializerJsTranslator.serializerTower(property: SerializableProper
?.let { expr -> if (property.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr }
}
private val classIdsWhichNeedKClass = setOf(enumSerializerId, contextSerializerId, polymorphicSerializerId)
internal fun SerializerJsTranslator.serializerInstance(
serializerClass: ClassDescriptor?,
module: ModuleDescriptor,
@@ -151,7 +146,7 @@ internal fun SerializerJsTranslator.serializerInstance(
if (serializerClass.kind == ClassKind.OBJECT) {
return context.serializerObjectGetter(serializerClass)
} else {
var args = if (serializerClass.classId in classIdsWhichNeedKClass)
var args = if (serializerClass.isSerializerWhichRequiersKClass())
listOf(ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!))
else kType.arguments.map {
val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi())
@@ -19,18 +19,13 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.js
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.backend.ast.JsExpression
import org.jetbrains.kotlin.js.backend.ast.JsInvocation
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
import org.jetbrains.kotlin.js.backend.ast.JsReturn
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor
import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor
import org.jetbrains.kotlin.psi.KtPureClassOrObject
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
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
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
class SerializableCompanionJsTranslator(
declaration: ClassDescriptor,
@@ -41,15 +36,20 @@ class SerializableCompanionJsTranslator(
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) {
val f = context.buildFunction(methodDescriptor) {jsFun, context ->
val serializer = serializableDescriptor.classSerializer!!
val stmt: JsExpression = if (serializer.kind == ClassKind.OBJECT) {
context.serializerObjectGetter(serializer)
} else {
val args = jsFun.parameters.map { JsNameRef(it.name) }
val ref = context.getInnerNameForDescriptor(
requireNotNull(
KSerializerDescriptorResolver.findSerializerConstructorForTypeArgumentsSerializers(serializer)
) { "Generated serializer does not have constructor with required number of arguments" })
JsInvocation(ref.makeRef(), args)
val stmt: JsExpression = when {
serializer.kind == ClassKind.OBJECT -> context.serializerObjectGetter(serializer)
serializer.isSerializerWhichRequiersKClass() -> JsNew(
context.translateQualifiedReference(serializer),
listOf(ExpressionVisitor.getObjectKClass(context, serializableDescriptor))
)
else -> {
val args = jsFun.parameters.map { JsNameRef(it.name) }
val ref = context.getInnerNameForDescriptor(
requireNotNull(
KSerializerDescriptorResolver.findSerializerConstructorForTypeArgumentsSerializers(serializer)
) { "Generated serializer does not have constructor with required number of arguments" })
JsInvocation(ref.makeRef(), args)
}
}
+JsReturn(stmt)
}
@@ -25,17 +25,24 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.constants.KClassValue
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
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.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.packageFqName
fun isAllowedToHaveAutoGeneratedSerializerMethods(classDescriptor: ClassDescriptor, serializableClassDescriptor: ClassDescriptor): Boolean {
internal fun isAllowedToHaveAutoGeneratedSerializerMethods(
classDescriptor: ClassDescriptor,
serializableClassDescriptor: ClassDescriptor
): Boolean {
// don't generate automatically anything for enums or interfaces or other strange things
if (serializableClassDescriptor.kind != ClassKind.CLASS) return false
// it is either GeneratedSerializer implementation
@@ -44,17 +51,17 @@ fun isAllowedToHaveAutoGeneratedSerializerMethods(classDescriptor: ClassDescript
(classDescriptor.typeConstructor.supertypes.any(::isKSerializer) && classDescriptor.declaredTypeParameters.isEmpty())
}
fun isKSerializer(type: KotlinType?): Boolean =
internal fun isKSerializer(type: KotlinType?): Boolean =
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, SerialEntityNames.KSERIALIZER_NAME_FQ)
fun isGeneratedKSerializer(type: KotlinType?): Boolean =
internal fun isGeneratedKSerializer(type: KotlinType?): Boolean =
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, SerialEntityNames.GENERATED_SERIALIZER_FQ)
fun ClassDescriptor.getGeneratedSerializerDescriptor(): ClassDescriptor =
internal fun ClassDescriptor.getGeneratedSerializerDescriptor(): ClassDescriptor =
module.getClassFromInternalSerializationPackage(SerialEntityNames.GENERATED_SERIALIZER_CLASS.identifier)
fun ClassDescriptor.createSerializerTypeFor(argument: SimpleType, baseSerializerInterface: FqName): SimpleType {
internal fun ClassDescriptor.createSerializerTypeFor(argument: SimpleType, baseSerializerInterface: FqName): SimpleType {
val projectionType = Variance.INVARIANT
val types = listOf(TypeProjectionImpl(projectionType, argument))
val descriptor = module.findClassAcrossModuleDependencies(ClassId.topLevel(baseSerializerInterface))
@@ -79,16 +86,13 @@ internal val DeclarationDescriptor.serializerForClass: KotlinType?
internal val ClassDescriptor.isSerialInfoAnnotation: Boolean
get() = annotations.hasAnnotation(serialInfoFqName)
val Annotations.serialNameValue: String?
get() {
val value = findAnnotationConstantValue<String?>(SerializationAnnotations.serialNameAnnotationFqName, "value")
return value
}
internal val Annotations.serialNameValue: String?
get() = findAnnotationConstantValue(SerializationAnnotations.serialNameAnnotationFqName, "value")
val Annotations.serialRequired: Boolean
internal val Annotations.serialRequired: Boolean
get() = hasAnnotation(SerializationAnnotations.requiredAnnotationFqName)
val Annotations.serialTransient: Boolean
internal val Annotations.serialTransient: Boolean
get() = hasAnnotation(SerializationAnnotations.serialTransientFqName)
// ----------------------------------------
@@ -100,7 +104,7 @@ val KotlinType?.toClassDescriptor: ClassDescriptor?
internal val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean
get() = this.kind == ClassKind.CLASS && annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)
val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking
internal val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking
get() {
if (kind != ClassKind.CLASS) return false
return hasSerializableAnnotationWithoutArgs
@@ -151,6 +155,8 @@ internal val ClassDescriptor?.classSerializer: ClassDescriptor?
internal val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean
get() = companionObjectDescriptor?.serializerForClass == this.defaultType
internal fun ClassDescriptor.isSerializerWhichRequiersKClass() = classId in setOf(enumSerializerId, contextSerializerId, polymorphicSerializerId)
internal fun checkSerializerNullability(classType: KotlinType, serializerType: KotlinType): KotlinType {
val castedToKSerial = requireNotNull(
serializerType.supertypes().find { isKSerializer(it) },
@@ -162,7 +168,7 @@ internal fun checkSerializerNullability(classType: KotlinType, serializerType: K
}
// returns only user-overriden Serializer
val KotlinType.overridenSerializer: KotlinType?
internal val KotlinType.overridenSerializer: KotlinType?
get() {
val desc = this.toClassDescriptor ?: return null
(desc.serializableWith)?.let { return checkSerializerNullability(this, it) }
@@ -171,17 +177,17 @@ val KotlinType.overridenSerializer: KotlinType?
return null
}
val KotlinType.genericIndex: Int?
internal val KotlinType.genericIndex: Int?
get() = (this.constructor.declarationDescriptor as? TypeParameterDescriptor)?.index
fun getSerializableClassDescriptorByCompanion(thisDescriptor: ClassDescriptor): ClassDescriptor? {
internal fun getSerializableClassDescriptorByCompanion(thisDescriptor: ClassDescriptor): ClassDescriptor? {
if (!thisDescriptor.isCompanionObject) return null
val classDescriptor = (thisDescriptor.containingDeclaration as? ClassDescriptor) ?: return null
if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null
return classDescriptor
}
fun getSerializableClassDescriptorBySerializer(serializerDescriptor: ClassDescriptor): ClassDescriptor? {
internal fun getSerializableClassDescriptorBySerializer(serializerDescriptor: ClassDescriptor): ClassDescriptor? {
val serializerForClass = serializerDescriptor.serializerForClass
if (serializerForClass != null) return serializerForClass.toClassDescriptor
if (serializerDescriptor.name !in setOf(
@@ -194,21 +200,22 @@ fun getSerializableClassDescriptorBySerializer(serializerDescriptor: ClassDescri
return classDescriptor
}
fun ClassDescriptor.checkSerializableClassPropertyResult(prop: PropertyDescriptor): Boolean =
internal fun ClassDescriptor.checkSerializableClassPropertyResult(prop: PropertyDescriptor): Boolean =
prop.returnType!!.isSubtypeOf(getClassFromSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS).toSimpleType(false)) // todo: cache lookup
// todo: serialization: do an actual check better that just number of parameters
fun ClassDescriptor.checkSaveMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
internal fun ClassDescriptor.checkSaveMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
parameters.size == 2
fun ClassDescriptor.checkSaveMethodResult(type: KotlinType): Boolean =
internal fun ClassDescriptor.checkSaveMethodResult(type: KotlinType): Boolean =
KotlinBuiltIns.isUnit(type)
// todo: serialization: do an actual check better that just number of parameters
fun ClassDescriptor.checkLoadMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
internal fun ClassDescriptor.checkLoadMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
parameters.size == 1
fun ClassDescriptor.checkLoadMethodResult(type: KotlinType): Boolean = getSerializableClassDescriptorBySerializer(this)?.defaultType == type
internal fun ClassDescriptor.checkLoadMethodResult(type: KotlinType): Boolean =
getSerializableClassDescriptorBySerializer(this)?.defaultType == type
// ----------------
@@ -217,7 +224,7 @@ inline fun <reified R> Annotations.findAnnotationConstantValue(annotationFqName:
annotation.allValueArguments.entries.singleOrNull { it.key.asString() == property }?.value?.value
} as? R
fun Annotations.findAnnotationKotlinTypeValue(
internal fun Annotations.findAnnotationKotlinTypeValue(
annotationFqName: FqName,
moduleForResolve: ModuleDescriptor,
property: String
@@ -229,13 +236,13 @@ fun Annotations.findAnnotationKotlinTypeValue(
// Search utils
fun ClassDescriptor.getKSerializerConstructorMarker(): ClassDescriptor =
internal fun ClassDescriptor.getKSerializerConstructorMarker(): ClassDescriptor =
module.findClassAcrossModuleDependencies(ClassId(packageFqName, SerialEntityNames.SERIAL_CTOR_MARKER_NAME))!!
fun ModuleDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
internal fun ModuleDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
getFromPackage(SerializationPackages.internalPackageFqName, classSimpleName)
fun ModuleDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
internal fun ModuleDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
getFromPackage(SerializationPackages.packageFqName, classSimpleName)
private fun ModuleDescriptor.getFromPackage(packageFqName: FqName, classSimpleName: String) = requireNotNull(
@@ -247,15 +254,15 @@ private fun ModuleDescriptor.getFromPackage(packageFqName: FqName, classSimpleNa
)
) { "Can't locate class $classSimpleName from package $packageFqName" }
fun ClassDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
internal fun ClassDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
requireNotNull(module.findClassAcrossModuleDependencies(ClassId(packageFqName, Name.identifier(classSimpleName)))) {"Can't locate class $classSimpleName"}
fun ClassDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
internal fun ClassDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
module.getClassFromInternalSerializationPackage(classSimpleName)
fun ClassDescriptor.toSimpleType(nullable: Boolean = true) = KotlinTypeFactory.simpleType(Annotations.EMPTY, this.typeConstructor, emptyList(), nullable)
fun Annotated.annotationsWithArguments(): List<Triple<ClassDescriptor, List<ValueArgument>, List<ValueParameterDescriptor>>> =
internal fun Annotated.annotationsWithArguments(): List<Triple<ClassDescriptor, List<ValueArgument>, List<ValueParameterDescriptor>>> =
annotations.asSequence()
.filter { it.type.toClassDescriptor?.isSerialInfoAnnotation == true }
.filterIsInstance<LazyAnnotationDescriptor>()