From acd6180c3051742975582922796d6343596b3f38 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Wed, 10 Aug 2022 18:59:18 +0200 Subject: [PATCH] Support generic classes in serialization FIR plugin --- .../compiler/backend/ir/BaseIrGenerator.kt | 2 +- .../backend/ir/IrBuilderWithPluginContext.kt | 258 ++++-------------- .../compiler/backend/ir/IrPredicates.kt | 2 + .../ir/SerialInfoImplJvmIrGenerator.kt | 256 ++++++++++++++++- .../backend/ir/SerializerForEnumsGenerator.kt | 2 +- .../ir/SerializerForInlineClassGenerator.kt | 3 +- .../backend/ir/SerializerIrGenerator.kt | 70 ++--- .../compiler/fir/FirBuildersUtils.kt | 74 +++-- .../fir/SerializationFirResolveExtension.kt | 95 ++++--- ...SerializationFirBlackBoxTestGenerated.java | 6 + .../SerializationFirMembersTestGenerated.java | 6 + .../testData/codegen/Basic.ir.txt | 42 ++- .../testData/codegen/Basic.kt | 2 +- .../testData/codegen/Basic.txt | 21 +- .../testData/codegen/Delegated.ir.txt | 5 +- .../testData/codegen/Sealed.ir.txt | 10 +- .../classWithCompanionObject.fir.txt | 4 +- .../classWithGenericParameters.fir.txt | 61 +++++ .../firMembers/classWithGenericParameters.kt | 23 ++ .../firMembers/defaultProperties.fir.txt | 4 +- .../firMembers/multipleProperties.fir.txt | 4 +- 21 files changed, 594 insertions(+), 356 deletions(-) create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.fir.txt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.kt diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt index f63a1c481ad..c6e81dd445c 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt @@ -305,7 +305,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override property.type, genericIndex = property.genericIndex ) { it, _ -> - val (_, ir) = generator.localSerializersFieldsDescriptors[it] + val ir = generator.localSerializersFieldsDescriptors[it] irGetField(irGet(dispatchReceiverParameter), ir.backingField!!) }?.let { expr -> wrapWithNullableSerializerIfNeeded(property.type, expr, nullableSerClass) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt index 2fa6740bedd..289640f010c 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt @@ -7,11 +7,12 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.builders.declarations.buildFun +import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.deepCopyWithVariables import org.jetbrains.kotlin.ir.expressions.* @@ -33,7 +34,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext -import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_FQ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_MODE_FQ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_PUBLICATION_MODE_NAME @@ -99,15 +99,7 @@ interface IrBuilderWithPluginContext { val lazyIrClass = compilerContext.referenceClass(ClassId.topLevel(LAZY_FQ))!!.owner val lazyIrType = lazyIrClass.defaultType.substitute(mapOf(lazyIrClass.typeParameters[0].symbol to targetIrType)) - val propertyDescriptor = - KSerializerDescriptorResolver.createValPropertyDescriptor( - Name.identifier(name.asString() + "\$delegate"), - containingClass.descriptor, - lazyIrType.toKotlinType(), - createGetter = true - ) - - return generateSimplePropertyWithBackingField(propertyDescriptor, containingClass).apply { + return generateSimplePropertyWithBackingField(Name.identifier(name.asString() + "\$delegate"), lazyIrType, containingClass).apply { val builder = DeclarationIrBuilder(compilerContext, containingClass.symbol, startOffset, endOffset) val initializerBody = builder.run { val enumElement = IrGetEnumValueImpl( @@ -133,11 +125,7 @@ interface IrBuilderWithPluginContext { name: Name, initializerBuilder: IrBlockBodyBuilder.() -> Unit ): IrProperty { - val targetKotlinType = type.toKotlinType() - val propertyDescriptor = - KSerializerDescriptorResolver.createValPropertyDescriptor(name, companionClass.descriptor, targetKotlinType) - - return generateSimplePropertyWithBackingField(propertyDescriptor, companionClass, name).apply { + return generateSimplePropertyWithBackingField(name, type, companionClass).apply { companionClass.contributeAnonymousInitializer { val irBlockBody = irBlockBody(startOffset, endOffset, initializerBuilder) irBlockBody.statements.dropLast(1).forEach { +it } @@ -292,209 +280,61 @@ interface IrBuilderWithPluginContext { } } - private inline fun IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? { - return declarations.singleOrNull { it.descriptor == descriptor } as? T - } - fun generateSimplePropertyWithBackingField( - propertyDescriptor: PropertyDescriptor, + propertyName: Name, + propertyType: IrType, propertyParent: IrClass, - fieldName: Name = propertyDescriptor.name, + visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE + ): IrProperty = generatePropertyMissingParts(null, propertyName, propertyType, propertyParent, visibility) + + fun generatePropertyMissingParts( + property: IrProperty?, + propertyName: Name, + propertyType: IrType, + propertyParent: IrClass, + visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE ): IrProperty { - val irProperty = propertyParent.searchForDeclaration(propertyDescriptor) ?: run { - with(propertyDescriptor) { - propertyParent.factory.createProperty( - propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrPropertySymbolImpl(propertyDescriptor), - name, visibility, modality, isVar, isConst, isLateInit, isDelegated, isExternal - ).also { - it.parent = propertyParent - propertyParent.addMember(it) - } - } + val field = property?.backingField ?: propertyParent.factory.buildField { + startOffset = propertyParent.startOffset + endOffset = propertyParent.endOffset + name = propertyName + type = propertyType + origin = SERIALIZABLE_PLUGIN_ORIGIN + isFinal = true + this.visibility = DescriptorVisibilities.PRIVATE + }.also { it.parent = propertyParent } + + val prop = property ?: propertyParent.addProperty { + startOffset = propertyParent.startOffset + endOffset = propertyParent.endOffset + name = propertyName + this.isVar = false + origin = SERIALIZABLE_PLUGIN_ORIGIN } - propertyParent.generatePropertyBackingFieldIfNeeded(propertyDescriptor, irProperty, fieldName) - val fieldSymbol = irProperty.backingField!!.symbol - irProperty.getter = propertyDescriptor.getter?.let { - propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = true) - }?.apply { parent = propertyParent } - irProperty.setter = propertyDescriptor.setter?.let { - propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = false) - }?.apply { parent = propertyParent } - return irProperty - } - - fun IrType.kClassToJClassIfNeeded(): IrType = this - - fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression = irExpression - - private fun IrClass.generatePropertyBackingFieldIfNeeded( - propertyDescriptor: PropertyDescriptor, - originProperty: IrProperty, - name: Name, - ) { - if (originProperty.backingField != null) return - - val field = with(propertyDescriptor) { - @OptIn(FirIncompatiblePluginAPI::class)// should be called only with old FE - originProperty.factory.createField( - originProperty.startOffset, originProperty.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrFieldSymbolImpl(propertyDescriptor), name, type.toIrType(), - visibility, !isVar, isEffectivelyExternal(), dispatchReceiverParameter == null - ) - } - field.apply { - parent = this@generatePropertyBackingFieldIfNeeded - correspondingPropertySymbol = originProperty.symbol + prop.apply { + field.correspondingPropertySymbol = this.symbol + backingField = field } - originProperty.backingField = field - } - - private fun IrClass.generatePropertyAccessor( - propertyDescriptor: PropertyDescriptor, - property: IrProperty, - descriptor: PropertyAccessorDescriptor, - fieldSymbol: IrFieldSymbol, - isGetter: Boolean, - ): IrSimpleFunction { - val irAccessor: IrSimpleFunction = when (isGetter) { - true -> searchForDeclaration(propertyDescriptor)?.getter - false -> searchForDeclaration(propertyDescriptor)?.setter - } ?: run { - with(descriptor) { - @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend - property.factory.createFunction( - fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor), - name, visibility, modality, returnType!!.toIrType(), - isInline, isEffectivelyExternal(), isTailrec, isSuspend, isOperator, isInfix, isExpect - ) - }.also { f -> - generateOverriddenFunctionSymbols(f, compilerContext.symbolTable) - f.createParameterDeclarations(descriptor) - @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend - f.returnType = descriptor.returnType!!.toIrType() - f.correspondingPropertySymbol = fieldSymbol.owner.correspondingPropertySymbol - } + val getter = prop.getter ?: prop.addGetter { + startOffset = propertyParent.startOffset + endOffset = propertyParent.endOffset + returnType = propertyType + origin = SERIALIZABLE_PLUGIN_ORIGIN + this.visibility = visibility + modality = Modality.FINAL } - irAccessor.body = when (isGetter) { - true -> generateDefaultGetterBody(irAccessor) - false -> generateDefaultSetterBody(irAccessor) + getter.apply { + if (dispatchReceiverParameter == null) + dispatchReceiverParameter = propertyParent.thisReceiver!!.copyTo(this, type = propertyParent.defaultType) + if (body == null) + body = compilerContext.irBuiltIns.createIrBuilder(symbol, propertyParent.startOffset, propertyParent.endOffset).irBlockBody { + +irReturn(irGetField(irGet(dispatchReceiverParameter!!), field)) + } } - - return irAccessor - } - - private fun generateDefaultGetterBody( - irAccessor: IrSimpleFunction - ): IrBlockBody { - val irProperty = irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}") - - val startOffset = irAccessor.startOffset - val endOffset = irAccessor.endOffset - val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset) - - val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol) - - val propertyIrType = irAccessor.returnType - irBody.statements.add( - IrReturnImpl( - startOffset, endOffset, compilerContext.irBuiltIns.nothingType, - irAccessor.symbol, - IrGetFieldImpl( - startOffset, endOffset, - irProperty.backingField?.symbol ?: error("Property expected to have backing field"), - propertyIrType, - receiver - ).let { - if (propertyIrType.isKClass()) { - irAccessor.returnType = irAccessor.returnType.kClassToJClassIfNeeded() - kClassExprToJClassIfNeeded(startOffset, endOffset, it) - } else it - } - ) - ) - return irBody - } - - private fun generateDefaultSetterBody( - irAccessor: IrSimpleFunction - ): IrBlockBody { - val irProperty = irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}") - val startOffset = irAccessor.startOffset - val endOffset = irAccessor.endOffset - val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset) - - val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol) - - val irValueParameter = irAccessor.valueParameters.single() - irBody.statements.add( - IrSetFieldImpl( - startOffset, endOffset, - irProperty.backingField?.symbol ?: error("Property ${irProperty.render()} expected to have backing field"), - receiver, - IrGetValueImpl(startOffset, endOffset, irValueParameter.type, irValueParameter.symbol), - compilerContext.irBuiltIns.unitType - ) - ) - return irBody - } - - fun generateReceiverExpressionForFieldAccess( - ownerSymbol: IrValueSymbol - ): IrExpression = IrGetValueImpl( - ownerSymbol.owner.startOffset, ownerSymbol.owner.endOffset, - ownerSymbol - ) - - fun IrFunction.createParameterDeclarations( - descriptor: FunctionDescriptor, - overwriteValueParameters: Boolean = false, - copyTypeParameters: Boolean = true - ) { - val function = this - fun irValueParameter(descriptor: ParameterDescriptor): IrValueParameter = with(descriptor) { - @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend - factory.createValueParameter( - function.startOffset, function.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this), - name, indexOrMinusOne, type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline, - isHidden = false, isAssignable = false - ).also { - it.parent = function - } - } - - if (copyTypeParameters) { - assert(typeParameters.isEmpty()) - copyTypeParamsFromDescriptor(descriptor) - } - - dispatchReceiverParameter = descriptor.dispatchReceiverParameter?.let { irValueParameter(it) } - extensionReceiverParameter = descriptor.extensionReceiverParameter?.let { irValueParameter(it) } - - if (!overwriteValueParameters) - assert(valueParameters.isEmpty()) - - valueParameters = descriptor.valueParameters.map { irValueParameter(it) } - } - - fun IrFunction.copyTypeParamsFromDescriptor(descriptor: FunctionDescriptor) { - val newTypeParameters = descriptor.typeParameters.map { - factory.createTypeParameter( - startOffset, endOffset, - SERIALIZABLE_PLUGIN_ORIGIN, - IrTypeParameterSymbolImpl(it), - it.name, it.index, it.isReified, it.variance - ).also { typeParameter -> - typeParameter.parent = this - } - } - @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend - newTypeParameters.forEach { typeParameter -> - typeParameter.superTypes = typeParameter.descriptor.upperBounds.map { it.toIrType() } - } - - typeParameters = newTypeParameters + return prop } fun createClassReference(classType: IrType, startOffset: Int, endOffset: Int): IrClassReference { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt index 0ab0920d848..2bfe6f32387 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt @@ -35,6 +35,8 @@ internal fun IrType.isKSerializer(): Boolean { return fqName == SerialEntityNames.KSERIALIZER_NAME_FQ || fqName == SerialEntityNames.GENERATED_SERIALIZER_FQ } +internal fun IrType.isGeneratedKSerializer(): Boolean = classifierOrNull?.isClassWithFqName(SerialEntityNames.GENERATED_SERIALIZER_FQ.toUnsafe()) == true + internal val IrClass.isInternalSerializable: Boolean get() { if (kind != ClassKind.CLASS) return false diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt index ac81e449df7..fbe7d6a1d28 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt @@ -7,32 +7,29 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI import org.jetbrains.kotlin.backend.common.ir.addExtensionReceiver -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.DescriptorVisibilities -import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.declarations.* -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import org.jetbrains.kotlin.ir.declarations.IrPackageFragment -import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl +import org.jetbrains.kotlin.ir.expressions.IrBlockBody import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol +import org.jetbrains.kotlin.ir.symbols.IrValueSymbol +import org.jetbrains.kotlin.ir.symbols.impl.* import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.constructors -import org.jetbrains.kotlin.ir.util.createImplicitParameterDeclarationWithWrappedDescriptor -import org.jetbrains.kotlin.ir.util.defaultType -import org.jetbrains.kotlin.ir.util.kotlinFqName +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames @@ -115,13 +112,13 @@ class SerialInfoImplJvmIrGenerator( putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name)) } - override fun IrType.kClassToJClassIfNeeded(): IrType = when { + private fun IrType.kClassToJClassIfNeeded(): IrType = when { this.isKClass() -> javaLangType this.isKClassArray() -> compilerContext.irBuiltIns.arrayClass.typeWith(javaLangType) else -> this } - override fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression { + private fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression { val getterSymbol = kClassJava.owner.getter!!.symbol return IrCallImpl( startOffset, endOffset, @@ -149,7 +146,15 @@ class SerialInfoImplJvmIrGenerator( addGetter().apply { annotations = listOf( IrConstructorCallImpl.fromSymbolOwner(jvmName.typeWith(), jvmName.constructors.single()).apply { - putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, "getJavaClass")) + putValueArgument( + 0, + IrConstImpl.string( + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + context.irBuiltIns.stringType, + "getJavaClass" + ) + ) } ) addExtensionReceiver(context.irBuiltIns.kClassClass.starProjectedType) @@ -180,4 +185,227 @@ class SerialInfoImplJvmIrGenerator( createImplicitParameterDeclarationWithWrappedDescriptor() block(this) }.symbol + + private inline fun IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? { + return declarations.singleOrNull { it.descriptor == descriptor } as? T + } + + private fun generateSimplePropertyWithBackingField( + propertyDescriptor: PropertyDescriptor, + propertyParent: IrClass, + fieldName: Name = propertyDescriptor.name, + ): IrProperty { + val irProperty = propertyParent.searchForDeclaration(propertyDescriptor) ?: run { + with(propertyDescriptor) { + propertyParent.factory.createProperty( + propertyParent.startOffset, + propertyParent.endOffset, + SERIALIZABLE_PLUGIN_ORIGIN, + IrPropertySymbolImpl(propertyDescriptor), + name, + visibility, + modality, + isVar, + isConst, + isLateInit, + isDelegated, + isExternal + ).also { + it.parent = propertyParent + propertyParent.addMember(it) + } + } + } + + propertyParent.generatePropertyBackingFieldIfNeeded(propertyDescriptor, irProperty, fieldName) + val fieldSymbol = irProperty.backingField!!.symbol + irProperty.getter = propertyDescriptor.getter?.let { + propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = true) + }?.apply { parent = propertyParent } + irProperty.setter = propertyDescriptor.setter?.let { + propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = false) + }?.apply { parent = propertyParent } + return irProperty + } + + private fun IrClass.generatePropertyBackingFieldIfNeeded( + propertyDescriptor: PropertyDescriptor, + originProperty: IrProperty, + name: Name, + ) { + if (originProperty.backingField != null) return + + val field = with(propertyDescriptor) { + @OptIn(FirIncompatiblePluginAPI::class)// should be called only with old FE + originProperty.factory.createField( + originProperty.startOffset, + originProperty.endOffset, + SERIALIZABLE_PLUGIN_ORIGIN, + IrFieldSymbolImpl(propertyDescriptor), + name, + type.toIrType(), + visibility, + !isVar, + isEffectivelyExternal(), + dispatchReceiverParameter == null + ) + } + field.apply { + parent = this@generatePropertyBackingFieldIfNeeded + correspondingPropertySymbol = originProperty.symbol + } + + originProperty.backingField = field + } + + private fun IrClass.generatePropertyAccessor( + propertyDescriptor: PropertyDescriptor, + property: IrProperty, + descriptor: PropertyAccessorDescriptor, + fieldSymbol: IrFieldSymbol, + isGetter: Boolean, + ): IrSimpleFunction { + val irAccessor: IrSimpleFunction = when (isGetter) { + true -> searchForDeclaration(propertyDescriptor)?.getter + false -> searchForDeclaration(propertyDescriptor)?.setter + } ?: run { + with(descriptor) { + @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend + property.factory.createFunction( + fieldSymbol.owner.startOffset, + fieldSymbol.owner.endOffset, + SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor), + name, visibility, modality, returnType!!.toIrType(), + isInline, isEffectivelyExternal(), isTailrec, isSuspend, isOperator, isInfix, isExpect + ) + }.also { f -> + generateOverriddenFunctionSymbols(f, compilerContext.symbolTable) + f.createParameterDeclarations(descriptor) + @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend + f.returnType = descriptor.returnType!!.toIrType() + f.correspondingPropertySymbol = fieldSymbol.owner.correspondingPropertySymbol + } + } + + irAccessor.body = when (isGetter) { + true -> generateDefaultGetterBody(irAccessor) + false -> generateDefaultSetterBody(irAccessor) + } + + return irAccessor + } + + private fun generateDefaultGetterBody( + irAccessor: IrSimpleFunction + ): IrBlockBody { + val irProperty = + irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}") + + val startOffset = irAccessor.startOffset + val endOffset = irAccessor.endOffset + val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset) + + val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol) + + val propertyIrType = irAccessor.returnType + irBody.statements.add( + IrReturnImpl( + startOffset, endOffset, compilerContext.irBuiltIns.nothingType, + irAccessor.symbol, + IrGetFieldImpl( + startOffset, endOffset, + irProperty.backingField?.symbol ?: error("Property expected to have backing field"), + propertyIrType, + receiver + ).let { + if (propertyIrType.isKClass()) { + irAccessor.returnType = irAccessor.returnType.kClassToJClassIfNeeded() + kClassExprToJClassIfNeeded(startOffset, endOffset, it) + } else it + } + ) + ) + return irBody + } + + private fun generateDefaultSetterBody( + irAccessor: IrSimpleFunction + ): IrBlockBody { + val irProperty = + irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}") + val startOffset = irAccessor.startOffset + val endOffset = irAccessor.endOffset + val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset) + + val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol) + + val irValueParameter = irAccessor.valueParameters.single() + irBody.statements.add( + IrSetFieldImpl( + startOffset, endOffset, + irProperty.backingField?.symbol ?: error("Property ${irProperty.render()} expected to have backing field"), + receiver, + IrGetValueImpl(startOffset, endOffset, irValueParameter.type, irValueParameter.symbol), + compilerContext.irBuiltIns.unitType + ) + ) + return irBody + } + + private fun generateReceiverExpressionForFieldAccess( + ownerSymbol: IrValueSymbol + ): IrExpression = IrGetValueImpl( + ownerSymbol.owner.startOffset, ownerSymbol.owner.endOffset, + ownerSymbol + ) + + private fun IrFunction.createParameterDeclarations( + descriptor: FunctionDescriptor, + overwriteValueParameters: Boolean = false, + copyTypeParameters: Boolean = true + ) { + val function = this + fun irValueParameter(descriptor: ParameterDescriptor): IrValueParameter = with(descriptor) { + @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend + factory.createValueParameter( + function.startOffset, function.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this), + name, indexOrMinusOne, type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline, + isHidden = false, isAssignable = false + ).also { + it.parent = function + } + } + + if (copyTypeParameters) { + assert(typeParameters.isEmpty()) + copyTypeParamsFromDescriptor(descriptor) + } + + dispatchReceiverParameter = descriptor.dispatchReceiverParameter?.let { irValueParameter(it) } + extensionReceiverParameter = descriptor.extensionReceiverParameter?.let { irValueParameter(it) } + + if (!overwriteValueParameters) + assert(valueParameters.isEmpty()) + + valueParameters = descriptor.valueParameters.map { irValueParameter(it) } + } + + private fun IrFunction.copyTypeParamsFromDescriptor(descriptor: FunctionDescriptor) { + val newTypeParameters = descriptor.typeParameters.map { + factory.createTypeParameter( + startOffset, endOffset, + SERIALIZABLE_PLUGIN_ORIGIN, + IrTypeParameterSymbolImpl(it), + it.name, it.index, it.isReified, it.variance + ).also { typeParameter -> + typeParameter.parent = this + } + } + @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend + newTypeParameters.forEach { typeParameter -> + typeParameter.superTypes = typeParameter.descriptor.upperBounds.map { it.toIrType() } + } + + typeParameters = newTypeParameters + } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt index 687bfd43695..16ecceb0074 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt @@ -29,7 +29,7 @@ class SerializerForEnumsGenerator( irClass: IrClass, compilerContext: SerializationPluginContext, serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, -) : SerializerIrGenerator(irClass, compilerContext, null, serialInfoJvmGenerator) { +) : SerializerIrGenerator(irClass, compilerContext, null) { override fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc -> fun irThis(): IrExpression = IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt index 9327fc13302..ee17dc89ab8 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt @@ -23,8 +23,7 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames class SerializerForInlineClassGenerator( irClass: IrClass, compilerContext: SerializationPluginContext, - serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, -) : SerializerIrGenerator(irClass, compilerContext, null, serialInfoJvmGenerator) { +) : SerializerIrGenerator(irClass, compilerContext, null) { override fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc -> fun irThis(): IrExpression = IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol) 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 503417e9929..77966852738 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 @@ -12,7 +12,6 @@ import org.jetbrains.kotlin.backend.jvm.functionByName import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* @@ -41,6 +40,7 @@ import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPlug import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.LOAD import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SAVE import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_DECODER_CLASS @@ -55,14 +55,13 @@ open class SerializerIrGenerator( val irClass: IrClass, compilerContext: SerializationPluginContext, metadataPlugin: SerializationDescriptorSerializerPlugin?, - private val serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, ) : BaseIrGenerator(irClass, compilerContext) { protected val serializableIrClass = getSerializableClassDescriptorBySerializer(irClass)!! protected val serialName: String = serializableIrClass.serialName() protected val properties = serializablePropertiesForIrBackend(serializableIrClass, metadataPlugin) protected val serializableProperties = properties.serializableProperties - protected val isGeneratedSerializer = irClass.descriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) // TODO TODO + protected val isGeneratedSerializer = irClass.superTypes.any(IrType::isGeneratedKSerializer) protected val generatedSerialDescPropertyDescriptor = getProperty( SerialEntityNames.SERIAL_DESC_FIELD, @@ -71,7 +70,7 @@ open class SerializerIrGenerator( protected val anySerialDescProperty = getProperty( SerialEntityNames.SERIAL_DESC_FIELD, - ) { true } // TODO REMOVE TRUE + ) { true } // remove true? protected val irAnySerialDescProperty = anySerialDescProperty @@ -82,18 +81,19 @@ open class SerializerIrGenerator( return irClass.properties.singleOrNull { it.name.asString() == name && isReturnTypeOk(it) } } - var localSerializersFieldsDescriptors: List> = emptyList() - protected set - protected fun findLocalSerializersFieldDescriptors(): List { + var localSerializersFieldsDescriptors: List = emptyList() + private set + + // null if was not found — we're in FIR + private fun findLocalSerializersFieldDescriptors(): List { val count = serializableIrClass.typeParameters.size if (count == 0) return emptyList() val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" } - return propNames.mapNotNull { name -> + return propNames.map { name -> getProperty(name) { it.getter!!.returnType.isKSerializer() } } } - protected open val serialDescImplClass: IrClassSymbol = compilerContext.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL) @@ -106,11 +106,13 @@ open class SerializerIrGenerator( // how to (auto)create backing field and getter/setter? compilerContext.symbolTable.withReferenceScope(irClass) { - prop = generateSimplePropertyWithBackingField(desc.descriptor, irClass) + prop = generatePropertyMissingParts(desc, desc.name, serialDescImplClass.starProjectedType, irClass, desc.visibility) - // TODO: Do not use descriptors here - localSerializersFieldsDescriptors = findLocalSerializersFieldDescriptors().map { prop -> - prop.descriptor to generateSimplePropertyWithBackingField(prop.descriptor, irClass) + localSerializersFieldsDescriptors = findLocalSerializersFieldDescriptors().mapIndexed { i, prop -> + generatePropertyMissingParts( + prop, Name.identifier("${SerialEntityNames.typeArgPrefix}$i"), + compilerContext.getClassFromRuntime(KSERIALIZER_CLASS).starProjectedType, irClass + ) } } @@ -141,7 +143,8 @@ open class SerializerIrGenerator( // save local descriptor to field +irSetField( - generateReceiverExpressionForFieldAccess( + IrGetValueImpl( + startOffset, endOffset, thisAsReceiverParameter.symbol ), prop.backingField!!, @@ -218,11 +221,9 @@ open class SerializerIrGenerator( // store type arguments serializers in fields val thisAsReceiverParameter = irClass.thisReceiver!! ctor.valueParameters.forEachIndexed { index, param -> - val localSerial = localSerializersFieldsDescriptors[index].second.backingField!! + val localSerial = localSerializersFieldsDescriptors[index].backingField!! +irSetField( - generateReceiverExpressionForFieldAccess( - thisAsReceiverParameter.symbol - ), localSerial, irGet(param) + IrGetValueImpl(startOffset, endOffset, thisAsReceiverParameter.symbol), localSerial, irGet(param) ) } } @@ -243,7 +244,7 @@ open class SerializerIrGenerator( val typeParams = serializableIrClass.typeParameters.mapIndexed { idx, _ -> irGetField( irGet(irFun.dispatchReceiverParameter!!), - localSerializersFieldsDescriptors[idx].second.backingField!! + localSerializersFieldsDescriptors[idx].backingField!! ) } val kSerType = ((irFun.returnType as IrSimpleType).arguments.first() as IrTypeProjection).type @@ -268,7 +269,8 @@ open class SerializerIrGenerator( val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc") // fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): StructureEncoder - val beginFunc = encoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 } + val beginFunc = + encoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 } val call = irCall(beginFunc, type = kOutputClass.defaultType).mapValueParametersIndexed { _, _ -> irGet(localSerialDesc) @@ -283,9 +285,9 @@ open class SerializerIrGenerator( if (writeSelfFunction != null) { // extract Tx from KSerializer list val typeArgs = - localSerializersFieldsDescriptors.map { (_, ir) -> ir.backingField!!.type.cast().arguments.single().typeOrNull } + localSerializersFieldsDescriptors.map { ir -> ir.backingField!!.type.cast().arguments.single().typeOrNull } val args = mutableListOf(irGet(objectToSerialize), irGet(localOutput), irGet(localSerialDesc)) - args.addAll(localSerializersFieldsDescriptors.map { (_, ir) -> + args.addAll(localSerializersFieldsDescriptors.map { ir -> irGetField( irGet(saveFunc.dispatchReceiverParameter!!), ir.backingField!! @@ -304,7 +306,7 @@ open class SerializerIrGenerator( serializableProperties, objectToSerialize, localOutput, localSerialDesc, kOutputClass, ignoreIndexTo = -1, initializerAdapter ) { it, _ -> - val (_, ir) = localSerializersFieldsDescriptors[it] + val ir = localSerializersFieldsDescriptors[it] irGetField(irGet(saveFunc.dispatchReceiverParameter!!), ir.backingField!!) } } @@ -327,7 +329,7 @@ open class SerializerIrGenerator( whenHaveSerializer, whenDoNot, { it, _ -> - val (_, ir) = localSerializersFieldsDescriptors[it] + val ir = localSerializersFieldsDescriptors[it] irGetField(irGet(dispatchReceiver), ir.backingField!!) }, returnTypeHint @@ -401,7 +403,8 @@ open class SerializerIrGenerator( } //input = input.beginStructure(...) - val beginFunc = decoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 } + val beginFunc = + decoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 } val call = irInvoke( irGet(loadFunc.valueParameters[0]), beginFunc, @@ -422,7 +425,7 @@ open class SerializerIrGenerator( } to listOf( localSerialDesc.get(), irInt(index), innerSerial, serialPropertiesMap.getValue(property.ir).get() ) - }, {sti -> + }, { sti -> inputClass.functions.single { it.owner.name.asString() == "${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}" && it.owner.valueParameters.size == 2 @@ -461,7 +464,12 @@ open class SerializerIrGenerator( // throw exception on unknown field - val excClassRef = compilerContext.referenceConstructors(ClassId(SerializationPackages.packageFqName, Name.identifier(UNKNOWN_FIELD_EXC))) + val excClassRef = compilerContext.referenceConstructors( + ClassId( + SerializationPackages.packageFqName, + Name.identifier(UNKNOWN_FIELD_EXC) + ) + ) .single { it.owner.valueParameters.singleOrNull()?.type?.isInt() == true } +elseBranch( irThrow( @@ -594,7 +602,8 @@ open class SerializerIrGenerator( val save = irClass.findPluginGeneratedMethod(SAVE)?.let { generateSave(it); true } ?: false val load = irClass.findPluginGeneratedMethod(LOAD)?.let { generateLoad(it); true } ?: false irClass.findPluginGeneratedMethod(SerialEntityNames.CHILD_SERIALIZERS_GETTER.identifier)?.let { generateChildSerializersGetter(it) } - irClass.findPluginGeneratedMethod(SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER.identifier)?.let { generateTypeParamsSerializersGetter(it) } + irClass.findPluginGeneratedMethod(SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER.identifier) + ?.let { generateTypeParamsSerializersGetter(it) } if (!prop && (save || load)) generateSerialDesc() if (serializableIrClass.typeParameters.isNotEmpty()) { @@ -605,7 +614,6 @@ open class SerializerIrGenerator( } - companion object { fun generate( irClass: IrClass, @@ -620,8 +628,8 @@ open class SerializerIrGenerator( context, serialInfoJvmGenerator ) - serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context, serialInfoJvmGenerator) - else -> SerializerIrGenerator(irClass, context, metadataPlugin, serialInfoJvmGenerator) + serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context) + else -> SerializerIrGenerator(irClass, context, metadataPlugin) } generator.generate() irClass.addDefaultConstructorIfAbsent(context) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt index 9deb1b1e9d7..cd794d993c4 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt @@ -6,53 +6,40 @@ package org.jetbrains.kotlinx.serialization.compiler.fir import org.jetbrains.kotlin.GeneratedDeclarationKey -import org.jetbrains.kotlin.descriptors.EffectiveVisibility -import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr -import org.jetbrains.kotlin.fir.declarations.FirClass -import org.jetbrains.kotlin.fir.declarations.FirConstructor -import org.jetbrains.kotlin.fir.declarations.builder.FirSimpleFunctionBuilder +import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.builder.buildPrimaryConstructor -import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction -import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl -import org.jetbrains.kotlin.fir.declarations.origin -import org.jetbrains.kotlin.fir.declarations.utils.modality +import org.jetbrains.kotlin.fir.declarations.builder.buildTypeParameter +import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter +import org.jetbrains.kotlin.fir.declarations.utils.addDefaultBoundIfNecessary import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension import org.jetbrains.kotlin.fir.moduleData import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider +import org.jetbrains.kotlin.fir.scopes.impl.toConeType +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals -import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl -import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef -import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl -import org.jetbrains.kotlin.name.CallableId -import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.fir.types.constructClassLikeType +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames // FIXME KT-53096: this has to be shared (copied from plugin example) @OptIn(SymbolInternals::class) -fun FirDeclarationGenerationExtension.buildConstructor(classId: ClassId, isInner: Boolean, key: GeneratedDeclarationKey): FirConstructor { +fun FirDeclarationGenerationExtension.buildPrimaryConstructor(owner: FirClassSymbol<*>, isInner: Boolean, key: GeneratedDeclarationKey, status: FirDeclarationStatus): FirConstructor { + val classId = owner.classId val lookupTag = ConeClassLikeLookupTagImpl(classId) return buildPrimaryConstructor { moduleData = session.moduleData origin = key.origin - returnTypeRef = buildResolvedTypeRef { - type = ConeClassLikeTypeImpl( - lookupTag, - emptyArray(), - isNullable = false - ) - } - status = FirResolvedDeclarationStatusImpl( - Visibilities.Public, - Modality.FINAL, - EffectiveVisibility.Public - ) + returnTypeRef = owner.defaultType().newRef() + this.status = status symbol = FirConstructorSymbol(classId) if (isInner && classId.isNestedClass) { dispatchReceiverType = classId.parentClassId?.let { @@ -63,4 +50,31 @@ fun FirDeclarationGenerationExtension.buildConstructor(classId: ClassId, isInner }.also { it.containingClassForStaticMemberAttr = lookupTag } +} + +fun ConeKotlinType.newRef(): FirResolvedTypeRef = buildResolvedTypeRef { + type = this@newRef +} + +fun newSimpleTypeParameter(firSession: FirSession, containingDeclarationSymbol: FirBasedSymbol<*>, name: Name) = buildTypeParameter { + moduleData = firSession.moduleData + origin = SerializationPluginKey.origin + resolvePhase = FirResolvePhase.BODY_RESOLVE + variance = Variance.INVARIANT + this.name = name + symbol = FirTypeParameterSymbol() + this.containingDeclarationSymbol = containingDeclarationSymbol + isReified = false + addDefaultBoundIfNecessary() +} + +fun newSimpleValueParameter(firSession: FirSession, typeRef: FirResolvedTypeRef, name: Name) = buildValueParameter { + moduleData = firSession.moduleData + origin = SerializationPluginKey.origin + this.name = name + this.symbol = FirValueParameterSymbol(this.name) + returnTypeRef = typeRef + isCrossinline = false + isNoinline = false + isVararg = false } \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt index ddcabdfb78a..98aad444293 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt @@ -14,24 +14,22 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol import org.jetbrains.kotlin.fir.copy import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin -import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.builder.* import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl import org.jetbrains.kotlin.fir.declarations.origin -import org.jetbrains.kotlin.fir.declarations.utils.addDefaultBoundIfNecessary import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider import org.jetbrains.kotlin.fir.moduleData -import org.jetbrains.kotlin.fir.resolve.ScopeSession -import org.jetbrains.kotlin.fir.resolve.defaultType -import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes +import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.scopes.* +import org.jetbrains.kotlin.fir.scopes.impl.toConeType import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.constructStarProjectedType import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.types.ConeTypeProjection import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.classId import org.jetbrains.kotlin.fir.types.constructClassLikeType @@ -170,13 +168,24 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene dispatchReceiverType = owner.defaultType() // TODO: handle serializable objects - val serializableType = + val serializableClassSymbol = (owner.getContainingDeclarationSymbol(session) as? FirClassSymbol<*>) ?: error("Can't get outer class for $owner") - // TODO: Add value parameters & type parameters for parameterized classes + typeParameters.addAll(serializableClassSymbol.typeParameterSymbols.map { newSimpleTypeParameter(session, symbol, it.name) }) + val parametersAsArguments = typeParameters.map { it.toConeType() }.toTypedArray() + + valueParameters.addAll(List(serializableClassSymbol.typeParameterSymbols.size) { i -> + newSimpleValueParameter( + session, + kSerializerClassId.constructClassLikeType(arrayOf(parametersAsArguments[i]), false).newRef(), + Name.identifier("${SerialEntityNames.typeArgPrefix}$i") + ) + }) + + returnTypeRef = buildResolvedTypeRef { type = kSerializerClassId.constructClassLikeType( - arrayOf(serializableType.defaultType().toTypeProjection(Variance.INVARIANT)), + arrayOf(serializableClassSymbol.constructType(parametersAsArguments, false)), isNullable = false ) } @@ -217,8 +226,37 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene } override fun generateConstructors(context: MemberGenerationContext): List { - val constructor = buildConstructor(context.owner.classId, isInner = false, SerializationPluginKey) - return listOf(constructor.symbol) + val owner = context.owner + val defaultObjectConstructor = buildPrimaryConstructor( + owner, isInner = false, SerializationPluginKey, status = FirResolvedDeclarationStatusImpl( + Visibilities.Private, + Modality.FINAL, + EffectiveVisibility.PrivateInClass + ) + ) + if (owner.name == SerialEntityNames.SERIALIZER_CLASS_NAME && owner.typeParameterSymbols.isNotEmpty()) { + val parameterizedConstructor = buildConstructor { + moduleData = session.moduleData + origin = SerializationPluginKey.origin + returnTypeRef = defaultObjectConstructor.returnTypeRef + symbol = FirConstructorSymbol(owner.classId) + dispatchReceiverType = defaultObjectConstructor.dispatchReceiverType + status = FirResolvedDeclarationStatusImpl( + Visibilities.Private, + Modality.FINAL, + EffectiveVisibility.PrivateInFile // accessed from a companion + ) + valueParameters.addAll(owner.typeParameterSymbols.mapIndexed { i, typeParam -> + newSimpleValueParameter( + session, + kSerializerClassId.constructClassLikeType(arrayOf(typeParam.toConeType()), false).newRef(), + Name.identifier("${SerialEntityNames.typeArgPrefix}$i") + ) + }) + } + return listOf(defaultObjectConstructor.symbol, parameterizedConstructor.symbol) + } + return listOf(defaultObjectConstructor.symbol) } fun addSerializerImplClass( @@ -244,36 +282,19 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene typeParameters.addAll(owner.typeParameterSymbols.map { param -> - buildTypeParameter { - moduleData = session.moduleData - origin = SerializationPluginKey.origin - resolvePhase = FirResolvePhase.BODY_RESOLVE - variance = Variance.INVARIANT - name = param.name - symbol = FirTypeParameterSymbol() - containingDeclarationSymbol = this@buildRegularClass.symbol - isReified = false - addDefaultBoundIfNecessary() // there should be KSerializer but whatever - } + newSimpleTypeParameter(session, symbol, param.name) }) - superTypeRefs += buildResolvedTypeRef { - type = generatedSerializerClassId.constructClassLikeType(arrayOf(owner.constructStarProjectedType()), isNullable = false) - } + val parametersAsArguments = typeParameters.map { it.toConeType() }.toTypedArray() + superTypeRefs += generatedSerializerClassId.constructClassLikeType( + arrayOf( + owner.constructType( + parametersAsArguments, + isNullable = false + ) + ), isNullable = false + ).newRef() } - // TODO: add typed constructor -// val secondaryCtors = -// if (!hasTypeParams) -// emptyList() -// else -// listOf( -// KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor( -// serializerFirClass, -// thisDescriptor, -// typeParameters -// ) -// ) -// serializerFirClass.secondaryConstructors = secondaryCtors return serializerFirClass.symbol } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java index 5b49b323081..b468e957309 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java @@ -31,6 +31,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.kt"); } + @Test + @TestMetadata("classWithGenericParameters.kt") + public void testClassWithGenericParameters() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.kt"); + } + @Test @TestMetadata("defaultProperties.kt") public void testDefaultProperties() throws Exception { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java index c23ad9b1b31..92dd59a6c2b 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java @@ -30,6 +30,12 @@ public class SerializationFirMembersTestGenerated extends AbstractSerializationF runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.kt"); } + @Test + @TestMetadata("classWithGenericParameters.kt") + public void testClassWithGenericParameters() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.kt"); + } + @Test @TestMetadata("defaultProperties.kt") public void testDefaultProperties() throws Exception { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.ir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.ir.txt index 7772dae7d1e..b8fb050138d 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.ir.txt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.ir.txt @@ -1,7 +1,7 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer { public final static ListOfUsers$$serializer INSTANCE - public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor + private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor static void () { NEW (ListOfUsers$$serializer) @@ -23,8 +23,7 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa ICONST_0 INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V) ALOAD (0) - CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor) - PUTSTATIC (ListOfUsers$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;) + PUTSTATIC (ListOfUsers$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;) LABEL (L1) LINENUMBER (13) RETURN @@ -164,7 +163,14 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa LABEL (L1) } - public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() + public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() { + LABEL (L0) + LINENUMBER (12) + GETSTATIC (ListOfUsers$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;) + CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor) + ARETURN + LABEL (L1) + } public void serialize(kotlinx.serialization.encoding.Encoder encoder, ListOfUsers value) { LABEL (L0) @@ -320,7 +326,7 @@ public final class ListOfUsers : java/lang/Object { public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer { public final static OptionalUser$$serializer INSTANCE - public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor + private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor static void () { NEW (OptionalUser$$serializer) @@ -342,8 +348,7 @@ public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serializ ICONST_1 INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V) ALOAD (0) - CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor) - PUTSTATIC (OptionalUser$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;) + PUTSTATIC (OptionalUser$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;) LABEL (L1) LINENUMBER (10) RETURN @@ -471,7 +476,14 @@ public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serializ LABEL (L1) } - public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() + public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() { + LABEL (L0) + LINENUMBER (9) + GETSTATIC (OptionalUser$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;) + CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor) + ARETURN + LABEL (L1) + } public void serialize(kotlinx.serialization.encoding.Encoder encoder, OptionalUser value) { LABEL (L0) @@ -699,7 +711,7 @@ public final class OptionalUser : java/lang/Object { public final class User$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer { public final static User$$serializer INSTANCE - public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor + private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor static void () { NEW (User$$serializer) @@ -725,8 +737,7 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in ICONST_0 INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V) ALOAD (0) - CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor) - PUTSTATIC (User$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;) + PUTSTATIC (User$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;) LABEL (L1) LINENUMBER (7) RETURN @@ -876,7 +887,14 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in LABEL (L1) } - public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() + public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() { + LABEL (L0) + LINENUMBER (6) + GETSTATIC (User$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;) + CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor) + ARETURN + LABEL (L1) + } public void serialize(kotlinx.serialization.encoding.Encoder encoder, User value) { LABEL (L0) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.kt index a29ae9cd248..c9431352667 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.kt @@ -1,4 +1,4 @@ -// CURIOUS_ABOUT serialize, deserialize, write$Self, childSerializers, , +// CURIOUS_ABOUT serialize, deserialize, write$Self, childSerializers, , , getDescriptor // WITH_STDLIB import kotlinx.serialization.* diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.txt index b70b532e6d4..477e52d2007 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.txt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.txt @@ -145,7 +145,12 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa ARETURN } - public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() + public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() { + LABEL (L0) + GETSTATIC (ListOfUsers$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;) + ARETURN + LABEL (L1) + } public void serialize(kotlinx.serialization.encoding.Encoder encoder, ListOfUsers value) { LABEL (L0) @@ -428,7 +433,12 @@ public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serializ ARETURN } - public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() + public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() { + LABEL (L0) + GETSTATIC (OptionalUser$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;) + ARETURN + LABEL (L1) + } public void serialize(kotlinx.serialization.encoding.Encoder encoder, OptionalUser value) { LABEL (L0) @@ -794,7 +804,12 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in ARETURN } - public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() + public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() { + LABEL (L0) + GETSTATIC (User$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;) + ARETURN + LABEL (L1) + } public void serialize(kotlinx.serialization.encoding.Encoder encoder, User value) { LABEL (L0) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.ir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.ir.txt index a5743113856..a91808b9c84 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.ir.txt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.ir.txt @@ -11,7 +11,7 @@ public final class DelegatedKt : java/lang/Object { public final class Test$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer { public final static Test$$serializer INSTANCE - public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor + private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor static void () { NEW (Test$$serializer) @@ -27,8 +27,7 @@ public final class Test$$serializer : java/lang/Object, kotlinx/serialization/in CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer) ICONST_0 INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, , (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V) - CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor) - PUTSTATIC (Test$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;) + PUTSTATIC (Test$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;) LABEL (L1) LINENUMBER (13) RETURN diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Sealed.ir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Sealed.ir.txt index 9728edc7a9f..b897cf03d40 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Sealed.ir.txt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Sealed.ir.txt @@ -1,7 +1,7 @@ public final class Container$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer { public final static Container$$serializer INSTANCE - public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor + private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor static void () { NEW (Container$$serializer) @@ -23,8 +23,7 @@ public final class Container$$serializer : java/lang/Object, kotlinx/serializati ICONST_0 INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V) ALOAD (0) - CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor) - PUTSTATIC (Container$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;) + PUTSTATIC (Container$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;) LABEL (L1) LINENUMBER (19) RETURN @@ -479,7 +478,7 @@ public final class Result$Err : Result { public final class Result$OK$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer { public final static Result$OK$$serializer INSTANCE - public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor + private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor static void () { NEW (Result$OK$$serializer) @@ -501,8 +500,7 @@ public final class Result$OK$$serializer : java/lang/Object, kotlinx/serializati ICONST_0 INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V) ALOAD (0) - CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor) - PUTSTATIC (Result$OK$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;) + PUTSTATIC (Result$OK$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;) RETURN } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.fir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.fir.txt index b2df063dd82..4ee8df252db 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.fir.txt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.fir.txt @@ -10,7 +10,7 @@ FILE: classWithCompanionObject.kt public final companion object Companion : R|kotlin/Any| { public final fun serializer(): R|kotlinx/serialization/KSerializer| - public constructor(): R|SomeClass.Companion| + private constructor(): R|SomeClass.Companion| } @@ -24,7 +24,7 @@ FILE: classWithCompanionObject.kt public final fun childSerializers(): R|kotlin/Array>| - public constructor(): R|SomeClass.$serializer| + private constructor(): R|SomeClass.$serializer| } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.fir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.fir.txt new file mode 100644 index 00000000000..718d7a20bf4 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.fir.txt @@ -0,0 +1,61 @@ +FILE: classWithGenericParameters.kt + @R|kotlinx/serialization/Serializable|() public final class GenericBox : R|kotlin/Any| { + public constructor(i: R|kotlin/Int|, t: R|T|, vs: R|kotlin/collections/List|): R|GenericBox| { + super() + } + + public final val i: R|kotlin/Int| = R|/i| + public get(): R|kotlin/Int| + + public final val t: R|T| = R|/t| + public get(): R|T| + + public final val vs: R|kotlin/collections/List| = R|/vs| + public get(): R|kotlin/collections/List| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(typeSerial0: R|kotlinx/serialization/KSerializer|, typeSerial1: R|kotlinx/serialization/KSerializer|): R|kotlinx/serialization/KSerializer>| + + private constructor(): R|GenericBox.Companion| + + } + + public final class $serializer : R|kotlinx/serialization/internal/GeneratedSerializer>| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|GenericBox|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|GenericBox| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + public final fun typeParametersSerializers(): R|kotlin/Array>| + + private constructor(): R|GenericBox.$serializer| + + private constructor(typeSerial0: R|kotlinx/serialization/KSerializer|, typeSerial1: R|kotlinx/serialization/KSerializer|): R|GenericBox.$serializer| + + } + + } + public final fun box(): R|kotlin/String| { + lval box: R|GenericBox| = R|/GenericBox.GenericBox|(Int(42), String(foo), R|kotlin/collections/listOf|(vararg(Boolean(true), Boolean(false)))) + lval serial: R|kotlinx/serialization/KSerializer>| = Q|GenericBox|.R|/GenericBox.Companion.serializer|(Q|kotlin/String|.R|kotlinx/serialization/builtins/serializer|(), Q|kotlin/Boolean|.R|kotlinx/serialization/builtins/serializer|()) + lval target: R|kotlin/String| = String({"i":42,"t":"foo","vs":[true,false]}) + lval s: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString||>(R|/serial|, R|/box|) + when () { + !=(R|/target|, R|/s|) -> { + ^box (String(Incorrect serialization: ), R|/s|) + } + } + + lval decoded: R|GenericBox| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString||>(R|/serial|, R|/s|) + when () { + !=(R|/box|.R|SubstitutionOverride|, R|/decoded|.R|SubstitutionOverride|) || !=(R|/box|.R|SubstitutionOverride|>|, R|/decoded|.R|SubstitutionOverride|>|) -> { + ^box String(Incorrect deserialization) + } + } + + ^box String(OK) + } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.kt new file mode 100644 index 00000000000..d50e3f5b568 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.kt @@ -0,0 +1,23 @@ +// WITH_STDLIB + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.builtins.* + +@Serializable +class GenericBox( + val i: Int, + val t: T, + val vs: List +) + +fun box(): String { + val box = GenericBox(42, "foo", listOf(true, false)) + val serial = GenericBox.serializer(String.serializer(), Boolean.serializer()) + val target = """{"i":42,"t":"foo","vs":[true,false]}""" + val s = Json.encodeToString(serial, box) + if (target != s) return "Incorrect serialization: $s" + val decoded = Json.decodeFromString(serial, s) + if (box.t != decoded.t || box.vs != decoded.vs) return "Incorrect deserialization" + return "OK" +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.fir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.fir.txt index 1b122476bec..d490380085e 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.fir.txt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.fir.txt @@ -27,7 +27,7 @@ FILE: defaultProperties.kt public final companion object Companion : R|kotlin/Any| { public final fun serializer(): R|kotlinx/serialization/KSerializer| - public constructor(): R|SomeClass.Companion| + private constructor(): R|SomeClass.Companion| } @@ -41,7 +41,7 @@ FILE: defaultProperties.kt public final fun childSerializers(): R|kotlin/Array>| - public constructor(): R|SomeClass.$serializer| + private constructor(): R|SomeClass.$serializer| } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.fir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.fir.txt index d1a49ddb9b2..3ca2c9223ec 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.fir.txt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.fir.txt @@ -19,7 +19,7 @@ FILE: multipleProperties.kt public final companion object Companion : R|kotlin/Any| { public final fun serializer(): R|kotlinx/serialization/KSerializer| - public constructor(): R|SomeClass.Companion| + private constructor(): R|SomeClass.Companion| } @@ -33,7 +33,7 @@ FILE: multipleProperties.kt public final fun childSerializers(): R|kotlin/Array>| - public constructor(): R|SomeClass.$serializer| + private constructor(): R|SomeClass.$serializer| }