From a2465910a3199bd029fdb5950816cb88f7070365 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Thu, 21 Feb 2019 14:39:33 +0300 Subject: [PATCH] Instantiating of Polymorphic in Companion.serializer() for JS and Native --- .../ir/SerializableCompanionIrGenerator.kt | 33 ++++++---- .../compiler/backend/js/JsCodegenUtil.kt | 7 +- .../js/SerializableCompanionJsTranslator.kt | 34 +++++----- .../compiler/resolve/KSerializationUtil.kt | 65 ++++++++++--------- 4 files changed, 74 insertions(+), 65 deletions(-) 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 6894dddc89c..3698576be6b 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,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 = 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 = getter.valueParameters.map { irGet(it) } + irInvoke(null, ctor, *args.toTypedArray()) + } } +irReturn(expr) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt index 70c66871463..d9792eaa8a9 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt @@ -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()) 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 04ff8d86a3f..e5283d0f10d 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 @@ -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) } 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 4c0c9f24713..9b647030c91 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 @@ -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(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): Boolean = +internal fun ClassDescriptor.checkSaveMethodParameters(parameters: List): 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): Boolean = +internal fun ClassDescriptor.checkLoadMethodParameters(parameters: List): 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 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, List>> = +internal fun Annotated.annotationsWithArguments(): List, List>> = annotations.asSequence() .filter { it.type.toClassDescriptor?.isSerialInfoAnnotation == true } .filterIsInstance()