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 4e8491f076f..5eb07d2792d 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 @@ -30,7 +30,6 @@ abstract class SerializerCodegen( protected val serializerDescriptor: ClassDescriptor, bindingContext: BindingContext ) : AbstractSerialGenerator(bindingContext, serializerDescriptor) { - // protected val serializerDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext) val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!! protected val serialName: String = serializableDescriptor.annotations.serialNameValue ?: serializableDescriptor.fqNameUnsafe.asString() protected val properties = SerializableProperties(serializableDescriptor, bindingContext) @@ -61,9 +60,7 @@ abstract class SerializerCodegen( return true } - protected open fun generateChildSerializersGetter(function: FunctionDescriptor) { - // TODO() - } + protected abstract fun generateChildSerializersGetter(function: FunctionDescriptor) protected val generatedSerialDescPropertyDescriptor = getPropertyToGenerate( serializerDescriptor, SerialEntityNames.SERIAL_DESC_FIELD, @@ -73,9 +70,20 @@ abstract class SerializerCodegen( serializerDescriptor::checkSerializableClassPropertyResult ) { true } + val localSerializersFieldsDescriptors: List = findLocalSerializersFieldDescriptors() + + private fun findLocalSerializersFieldDescriptors(): List { + val count = serializableDescriptor.declaredTypeParameters.size + if (count == 0) return emptyList() + val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" } + return propNames.mapNotNull { name -> + getPropertyToGenerate(serializerDescriptor, name) { isKSerializer(it.returnType) } + } + } + protected abstract fun generateSerialDesc() - protected abstract fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) + protected abstract fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor) @@ -129,7 +137,6 @@ abstract class SerializerCodegen( ) .singleOrNull { property -> isKindOk(property.kind) && - property.modality != Modality.FINAL && property.returnType != null && isReturnTypeOk(property) } 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 23278203fb3..a1cc641dcd0 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 @@ -58,11 +58,19 @@ interface IrBuilderExtension { ) else compilerContext.externalSymbols.referenceSimpleFunction(descriptor).owner f.parent = this f.returnType = descriptor.returnType!!.toIrType() - f.createParameterDeclarations() + if (!fromStubs) f.createParameterDeclarations(this.thisReceiver) f.body = compilerContext.createIrBuilder(f.symbol).irBlockBody { bodyGen(f) } this.addMember(f) } + fun IrClass.contributeCtor(descriptor: ClassConstructorDescriptor, bodyGen: IrBlockBodyBuilder.(IrFunction) -> Unit) { + val c = compilerContext.externalSymbols.referenceConstructor(descriptor).owner + c.parent = this + c.returnType = descriptor.returnType.toIrType() + c.body = compilerContext.createIrBuilder(c.symbol).irBlockBody { bodyGen(c) } + this.addMember(c) + } + fun IrClass.contributeConstructor( descriptor: ClassConstructorDescriptor, bodyGen: IrBlockBodyBuilder.(IrConstructor) -> Unit @@ -369,7 +377,7 @@ interface IrBuilderExtension { } fun IrBuilderWithScope.serializerInstance( - enclosingGenerator: AbstractSerialGenerator, + enclosingGenerator: SerializerIrGenerator, serializableDescriptor: ClassDescriptor, serializerClass: ClassDescriptor?, module: ModuleDescriptor, @@ -380,7 +388,9 @@ interface IrBuilderExtension { compilerContext.externalSymbols.referenceClass(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer)) if (serializerClass == null) { if (genericIndex == null) return null - TODO("Saved serializer for generic argument") + val thiz = enclosingGenerator.irClass.thisReceiver!! + val prop = enclosingGenerator.localSerializersFieldsDescriptors[genericIndex] + return irGetField(irGet(thiz), compilerContext.localSymbolTable.referenceField(prop).owner) } if (serializerClass.kind == ClassKind.OBJECT) { return irGetObject(serializerClass) 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 868bffeb09a..21455c45c92 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 @@ -3,6 +3,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.expressions.IrExpression @@ -11,10 +12,8 @@ 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.classSerializer -import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion -import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable -import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor +import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.findSerializerConstructorForTypeArgumentsSerializers class SerializableCompanionIrGenerator( val irClass: IrClass, @@ -40,13 +39,16 @@ class SerializableCompanionIrGenerator( } override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) = - irClass.contributeFunction(methodDescriptor, fromStubs = true) { _ -> + irClass.contributeFunction(methodDescriptor, fromStubs = true) { getter -> val serializer = serializableDescriptor.classSerializer?.toClassDescriptor!! val expr = if (serializer.kind == ClassKind.OBJECT) { irGetObject(serializer) } else { - val ctor = compilerContext.externalSymbols.referenceConstructor(serializer.unsubstitutedPrimaryConstructor!!) - val args: List = emptyList() // todo + 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/ir/SerializerIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index 574a2aaf341..e01cecaa7e0 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -8,18 +8,15 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irThrow +import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrBlockBody import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl +import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.mapValueParameters import org.jetbrains.kotlin.ir.expressions.mapValueParametersIndexed import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol @@ -69,12 +66,17 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: introduceValueParameter(thisAsReceiverParameter) prop = generateSimplePropertyWithBackingField(thisAsReceiverParameter.symbol, desc, irClass) irClass.addMember(prop) + + localSerializersFieldsDescriptors.forEach { + irClass.addMember(generateSimplePropertyWithBackingField(thisAsReceiverParameter.symbol, it, irClass)) + } } compilerContext.localSymbolTable.declareAnonymousInitializer( irClass.startOffset, irClass.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, irClass.descriptor ).buildWithScope { initIrBody -> - val ctor = irClass.declarations.filterIsInstance().singleOrNull() + val ctor = irClass.declarations.filterIsInstance().find { it.isPrimary } + ?: throw AssertionError("Serializer must have primary constructor") val serialClassDescImplCtor = compilerContext.externalSymbols.referenceConstructor(serialDescImplConstructor) compilerContext.localSymbolTable.withScope(initIrBody.descriptor) { initIrBody.body = compilerContext.createIrBuilder(initIrBody.symbol).irBlockBody { @@ -106,16 +108,34 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: irGet(localDesc) ) } - // workaround for KT-25353 -// irClass.addMember(initIrBody) - (ctor?.body as? IrBlockBody)?.statements?.addAll(initIrBody.body.statements) + (ctor.body as? IrBlockBody)?.statements?.addAll(initIrBody.body.statements) } } } - override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) { -// TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } + override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) = + irClass.contributeCtor(typedConstructorDescriptor) { ctor -> + // generate call to primary ctor to init serialClassDesc and super() + val primaryCtor = irClass.descriptor.unsubstitutedPrimaryConstructor + ?: throw AssertionError("Serializer class must have primary constructor") + +IrDelegatingConstructorCallImpl( + startOffset, + endOffset, + compilerContext.irBuiltIns.unitType, + compilerContext.localSymbolTable.referenceConstructor(primaryCtor), + primaryCtor + ) + + // store type arguments serializers in fields + val thisAsReceiverParameter = irClass.thisReceiver!! + ctor.valueParameters.forEachIndexed { index, param -> + val localSerial = compilerContext.localSymbolTable.referenceField(localSerializersFieldsDescriptors[index]) + +irSetField(generateReceiverExpressionForFieldAccess( + thisAsReceiverParameter.symbol, + localSerializersFieldsDescriptors[index] + ), localSerial.owner, irGet(param)) + } + } override fun generateChildSerializersGetter(function: FunctionDescriptor) = irClass.contributeFunction(function) { irFun -> val allSerializers = orderedProperties.map { requireNotNull( diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Synthetics.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Synthetics.kt new file mode 100644 index 00000000000..5ae9ab15c5d --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Synthetics.kt @@ -0,0 +1,39 @@ +package org.jetbrains.kotlinx.serialization.compiler.backend.ir + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.FieldDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.KotlinType + + +/** + * Simple property descriptor with backing field without getters/setters for ir generation purposes + */ +class SimpleSyntheticPropertyDescriptor( + owner: ClassDescriptor, + name: String, + type: KotlinType, + isVar: Boolean = false, + visibility: Visibility = Visibilities.PRIVATE +) : PropertyDescriptorImpl( + owner, + null, + Annotations.EMPTY, + Modality.FINAL, + visibility, + isVar, + Name.identifier(name), + CallableMemberDescriptor.Kind.SYNTHESIZED, + owner.source, + false, false, false, false, false, false + ) { + + private val _backingField = FieldDescriptorImpl(Annotations.EMPTY, this) + + init { + super.setType(type, emptyList(), owner.thisAsReceiverParameter, null) + super.initialize(null, null, _backingField, null) + } +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt index 24e573247d9..b62574eae4a 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt @@ -107,7 +107,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor, translator.addProperty(propDesc, getterExpr, null) } - override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) { + override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) { val f = context.buildFunction(typedConstructorDescriptor) { jsFun, context -> val thiz = jsFun.scope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME).makeRef() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt index bd875db1c31..d35dcf1de5f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt @@ -50,7 +50,7 @@ class SerializerCodegenImpl( } } - override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) { + override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) { serializableDescriptor.declaredTypeParameters.forEachIndexed { i, _ -> codegen.v.newField( OtherOrigin(codegen.myClass.psiOrParent), ACC_PRIVATE or ACC_SYNTHETIC, 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 e4f6f95b8b6..0638478be0d 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 @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.types.TypeProjectionImpl import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.typeUtil.createProjection import org.jetbrains.kotlin.types.typeUtil.makeNullable +import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SimpleSyntheticPropertyDescriptor import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.IMPL_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_CLASS_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix @@ -133,9 +134,10 @@ object KSerializerDescriptorResolver { val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return if (name == SerialEntityNames.SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) && fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL } - ) + ) { result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor)) - + } + result.addAll(createLocalSerializersFieldsDescriptors(classDescriptor, thisDescriptor)) } fun generateCompanionObjectMethods( @@ -176,7 +178,7 @@ object KSerializerDescriptorResolver { } } - fun createSerializableClassPropertyDescriptor( + private fun createSerializableClassPropertyDescriptor( companionDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor ): PropertyDescriptor = @@ -338,7 +340,7 @@ object KSerializerDescriptorResolver { return constrDesc } - fun createSerializerGetterDescriptor(thisClass: ClassDescriptor, serializableClass: ClassDescriptor): SimpleFunctionDescriptor { + private fun createSerializerGetterDescriptor(thisClass: ClassDescriptor, serializableClass: ClassDescriptor): SimpleFunctionDescriptor { val f = SimpleFunctionDescriptorImpl.create( thisClass, Annotations.EMPTY, @@ -493,4 +495,19 @@ object KSerializerDescriptorResolver { ) } } + + // create properties typeSerial0, typeSerial1, etc... for storing generic arguments' serializers + private fun createLocalSerializersFieldsDescriptors(serializableDescriptor: ClassDescriptor, serializerDescriptor: ClassDescriptor): List { + if (serializableDescriptor.declaredTypeParameters.isEmpty()) return emptyList() + val serializerClass = serializableDescriptor.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS) + return serializerDescriptor.declaredTypeParameters.mapIndexed { index, param -> + val pType = + KotlinTypeFactory.simpleNotNullType( + Annotations.EMPTY, + serializerClass, + listOf(TypeProjectionImpl(param.defaultType)) + ) + SimpleSyntheticPropertyDescriptor(serializerDescriptor, "$typeArgPrefix$index", pType) + } + } }