From a4a1df0a81c518e464c3efce4821fcc7eb1e90bd Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Mon, 1 Oct 2018 15:25:02 +0300 Subject: [PATCH] Add information about secondary constructors to synthetic classes. Before this, descriptors for such constructors were created in-place where they needed by the codegens. However, presence of symbol table in IR backend requires the single instance of constructor descriptor across all compilation to be able to reference it and create a symbol. This support of generics in kotlinx.serialization on Kotlin/Native. --- .../codegen/ImplementationBodyCodegen.java | 4 +- .../SyntheticClassOrObjectDescriptor.kt | 9 ++-- .../backend/common/SerializerCodegen.kt | 26 +++--------- .../compiler/backend/ir/GeneratorHelpers.kt | 35 ++++++++-------- .../compiler/backend/js/JsCodegenUtil.kt | 4 +- .../js/SerializableCompanionJsTranslator.kt | 4 +- .../resolve/KSerializerDescriptorResolver.kt | 41 ++++++++++++++----- 7 files changed, 66 insertions(+), 57 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 92539538919..ebef33160d0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.name.ClassId; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.psi.synthetics.SyntheticClassOrObjectDescriptor; import org.jetbrains.kotlin.resolve.*; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; @@ -441,7 +442,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { try { lookupConstructorExpressionsInClosureIfPresent(); constructorCodegen.generatePrimaryConstructor(delegationFieldsInfo, superClassAsmType); - if (!descriptor.isInline()) { + if (!descriptor.isInline() && !(descriptor instanceof SyntheticClassOrObjectDescriptor)) { + // Synthetic classes does not have declarations for secondary constructors for (ClassConstructorDescriptor secondaryConstructor : DescriptorUtilsKt.getSecondaryConstructors(descriptor)) { constructorCodegen.generateSecondaryConstructor(secondaryConstructor, superClassAsmType); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/synthetics/SyntheticClassOrObjectDescriptor.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/synthetics/SyntheticClassOrObjectDescriptor.kt index fa5f8efb2c3..67731aafdc8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/synthetics/SyntheticClassOrObjectDescriptor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/synthetics/SyntheticClassOrObjectDescriptor.kt @@ -27,7 +27,6 @@ import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.types.AbstractClassTypeConstructor import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeConstructor -import java.lang.IllegalStateException /* * This class introduces all attributes that are needed for synthetic classes/object so far. @@ -52,6 +51,8 @@ class SyntheticClassOrObjectDescriptor( private val thisDescriptor: SyntheticClassOrObjectDescriptor get() = this // code readability private lateinit var typeParameters: List + public var secondaryConstructors: List = emptyList() + private val typeConstructor = SyntheticTypeConstructor(c.storageManager) private val resolutionScopesSupport = ClassResolutionScopesSupport(thisDescriptor, c.storageManager, c.languageVersionSettings, { outerScope }) private val syntheticSupertypes = @@ -62,7 +63,9 @@ class SyntheticClassOrObjectDescriptor( c.storageManager.createLazyValue { createUnsubstitutedPrimaryConstructor(constructorVisibility) } @JvmOverloads - fun initialize(typeParameters: List = emptyList()) { + fun initialize( + typeParameters: List = emptyList() + ) { this.typeParameters = typeParameters } @@ -81,7 +84,7 @@ class SyntheticClassOrObjectDescriptor( override fun getCompanionObjectDescriptor(): ClassDescriptorWithResolutionScopes? = null override fun getTypeConstructor(): TypeConstructor = typeConstructor override fun getUnsubstitutedPrimaryConstructor() = _unsubstitutedPrimaryConstructor() - override fun getConstructors() = listOf(_unsubstitutedPrimaryConstructor()) + override fun getConstructors() = listOf(_unsubstitutedPrimaryConstructor()) + secondaryConstructors override fun getDeclaredTypeParameters() = typeParameters override fun getStaticScope() = MemberScope.Empty override fun getUnsubstitutedMemberScope() = unsubstitutedMemberScope diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt index 35801ae664d..4e8491f076f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlinx.serialization.compiler.resolve.* -import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.findSerializerConstructorForTypeArgumentsSerializers abstract class SerializerCodegen( protected val serializerDescriptor: ClassDescriptor, @@ -45,8 +45,10 @@ abstract class SerializerCodegen( generateDescriptorGetterIfNeeded() // if (save || load || prop) // generateSerialDesc() - if (serializableDescriptor.declaredTypeParameters.isNotEmpty() && typedSerializerConstructorNotDeclared()) { - generateGenericFieldsAndConstructor(createTypedSerializerConstructorDescriptor(serializerDescriptor, serializableDescriptor)) + if (serializableDescriptor.declaredTypeParameters.isNotEmpty()) { + findSerializerConstructorForTypeArgumentsSerializers(serializerDescriptor)?.let { + generateGenericFieldsAndConstructor(it) + } } } @@ -63,24 +65,6 @@ abstract class SerializerCodegen( // TODO() } - // checks if user didn't declared constructor (KSerializer, KSerializer...) on a KSerializer> - private fun typedSerializerConstructorNotDeclared(): Boolean { - val serializableImplementationTypeArguments = extractKSerializerArgumentFromImplementation(serializerDescriptor)?.arguments - ?: throw AssertionError("Serializer does not implement KSerializer??") - - val typeParamsCount = serializableImplementationTypeArguments.size - if (typeParamsCount == 0) return false //don't need it - val ctors = serializerDescriptor.constructors - val found = - ctors.any { - it.valueParameters.size == typeParamsCount && it.valueParameters.foldIndexed(false) { index, flag, parameterDescriptor -> - val type = parameterDescriptor.type - flag || (isKSerializer(type) && type.arguments.first() == serializableImplementationTypeArguments[index]) - } - } - return !found - } - protected val generatedSerialDescPropertyDescriptor = getPropertyToGenerate( serializerDescriptor, SerialEntityNames.SERIAL_DESC_FIELD, serializerDescriptor::checkSerializableClassPropertyResult) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt index c6b308c8c12..23278203fb3 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.backend.common.BackendContext -import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET @@ -25,8 +24,6 @@ import org.jetbrains.kotlin.ir.types.IrType 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.backend.ast.JsExpression -import org.jetbrains.kotlin.js.backend.ast.JsNew import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.classId @@ -34,9 +31,6 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isTypeParameter 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.js.SerializerJsTranslator -import org.jetbrains.kotlinx.serialization.compiler.backend.js.serializerInstance -import org.jetbrains.kotlinx.serialization.compiler.backend.js.translateQualifiedReference 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.referenceArraySerializerId @@ -375,15 +369,15 @@ interface IrBuilderExtension { } fun IrBuilderWithScope.serializerInstance( - enclosingGenerator: AbstractSerialGenerator, - serializableDescriptor: ClassDescriptor, - serializerClass: ClassDescriptor?, - module: ModuleDescriptor, - kType: KotlinType, - genericIndex: Int? = null + enclosingGenerator: AbstractSerialGenerator, + serializableDescriptor: ClassDescriptor, + serializerClass: ClassDescriptor?, + module: ModuleDescriptor, + kType: KotlinType, + genericIndex: Int? = null ): IrExpression? { val nullableSerClass = - compilerContext.externalSymbols.referenceClass(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer)) + compilerContext.externalSymbols.referenceClass(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer)) if (serializerClass == null) { if (genericIndex == null) return null TODO("Saved serializer for generic argument") @@ -395,22 +389,25 @@ interface IrBuilderExtension { TODO("enum and context serializer") else kType.arguments.map { val argSer = enclosingGenerator.findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi()) - val expr = serializerInstance(enclosingGenerator, serializableDescriptor, argSer, module, it.type, it.type.genericIndex) ?: return null + val expr = serializerInstance(enclosingGenerator, serializableDescriptor, argSer, module, it.type, it.type.genericIndex) + ?: return null // todo: smth better than constructors[0] ?? if (it.type.isMarkedNullable) irInvoke(null, nullableSerClass.constructors.toList()[0], expr) else expr } if (serializerClass.classId == referenceArraySerializerId) TODO("reference array serializer") val serializable = getSerializableClassDescriptorBySerializer(serializerClass) val ctor = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) { - KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor(serializerClass, serializableDescriptor) - .let { compilerContext.externalSymbols.referenceConstructor(it) } + requireNotNull( + KSerializerDescriptorResolver.findSerializerConstructorForTypeArgumentsSerializers(serializerClass) + ) { "Generated serializer does not have constructor with required number of arguments" } + .let { compilerContext.externalSymbols.referenceConstructor(it) } } else { compilerContext.externalSymbols.referenceConstructor(serializerClass.unsubstitutedPrimaryConstructor!!) } return irInvoke( - null, - ctor, - *args.toTypedArray() + null, + ctor, + *args.toTypedArray() ) } } 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 941330189b5..1b84f685266 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 @@ -156,7 +156,9 @@ internal fun SerializerJsTranslator.serializerInstance( args = listOf(createGetKClassExpression(kType.arguments[0].type.toClassDescriptor!!)) + args val serializable = getSerializableClassDescriptorBySerializer(serializerClass) val ref = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) { - val desc = KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor(serializerClass, serializableDescriptor) + val desc = requireNotNull( + KSerializerDescriptorResolver.findSerializerConstructorForTypeArgumentsSerializers(serializerClass) + ) { "Generated serializer does not have constructor with required number of arguments" } JsInvocation(context.getInnerReference(desc), args) } else { JsNew(context.translateQualifiedReference(serializerClass), args) 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 5cf622f7bc4..73a595a5cb4 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 @@ -43,7 +43,9 @@ class SerializableCompanionJsTranslator( } else { val args = jsFun.parameters.map { JsNameRef(it.name) } val ref = context.getInnerNameForDescriptor( - KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor(serializer, serializableDescriptor)) + 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/KSerializerDescriptorResolver.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt index 635074aa6f5..e4f6f95b8b6 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt @@ -97,7 +97,8 @@ object KSerializerDescriptorResolver { ): ClassDescriptor { val thisDeclaration = declarationProvider.correspondingClassOrObject!! val scope = ctx.declarationScopeProvider.getResolutionScopeForDeclaration(declarationProvider.ownerInfo!!.scopeAnchor) - val serializerKind = if (thisDescriptor.declaredTypeParameters.isNotEmpty()) ClassKind.CLASS else ClassKind.OBJECT + val hasTypeParams = thisDescriptor.declaredTypeParameters.isNotEmpty() + val serializerKind = if (hasTypeParams) ClassKind.CLASS else ClassKind.OBJECT val serializerDescriptor = SyntheticClassOrObjectDescriptor( ctx, thisDeclaration, @@ -114,6 +115,12 @@ object KSerializerDescriptorResolver { ) } serializerDescriptor.initialize(typeParameters) + val secondaryCtors = + if (!hasTypeParams) + emptyList() + else + listOf(createTypedSerializerConstructorDescriptor(serializerDescriptor, thisDescriptor, typeParameters)) + serializerDescriptor.secondaryConstructors = secondaryCtors return serializerDescriptor } @@ -286,9 +293,22 @@ object KSerializerDescriptorResolver { return functionDescriptor } - fun createTypedSerializerConstructorDescriptor( + // finds constructor (KSerializer, KSerializer...) on a KSerializer> + fun findSerializerConstructorForTypeArgumentsSerializers(serializerDescriptor: ClassDescriptor): ClassConstructorDescriptor? { + val serializableImplementationTypeArguments = extractKSerializerArgumentFromImplementation(serializerDescriptor)?.arguments + ?: throw AssertionError("Serializer does not implement KSerializer??") + + val typeParamsCount = serializableImplementationTypeArguments.size + if (typeParamsCount == 0) return null //don't need it + return serializerDescriptor.constructors.find { ctor -> + ctor.valueParameters.size == typeParamsCount && ctor.valueParameters.all { isKSerializer(it.type) } + } + } + + private fun createTypedSerializerConstructorDescriptor( classDescriptor: ClassDescriptor, - serializableDescriptor: ClassDescriptor + serializableDescriptor: ClassDescriptor, + typeParameters: List ): ClassConstructorDescriptor { val constrDesc = ClassConstructorDescriptorImpl.createSynthesized( classDescriptor, @@ -297,24 +317,23 @@ object KSerializerDescriptorResolver { classDescriptor.source ) val serializerClass = classDescriptor.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS) - val targs = mutableListOf() + assert(serializableDescriptor.declaredTypeParameters.size == typeParameters.size) val args = serializableDescriptor.declaredTypeParameters.mapIndexed { index, param -> - val targ = TypeParameterDescriptorImpl.createWithDefaultBound( - constrDesc, Annotations.EMPTY, false, Variance.INVARIANT, - param.name, index - ) val pType = - KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, serializerClass, listOf(TypeProjectionImpl(targ.defaultType))) + KotlinTypeFactory.simpleNotNullType( + Annotations.EMPTY, + serializerClass, + listOf(TypeProjectionImpl(typeParameters[index].defaultType)) + ) - targs.add(targ) ValueParameterDescriptorImpl( constrDesc, null, index, Annotations.EMPTY, Name.identifier("$typeArgPrefix$index"), pType, false, false, false, null, constrDesc.source ) } - constrDesc.initialize(args, Visibilities.PUBLIC, targs) + constrDesc.initialize(args, Visibilities.PUBLIC, typeParameters) constrDesc.returnType = classDescriptor.defaultType return constrDesc }