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 }