From f1b1837f409fc3c19e10daedf60fde634a4280c1 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Fri, 7 Oct 2022 19:15:00 +0200 Subject: [PATCH] Rework SerialInfo$Impl annotation implementation to be available in FIR: - IR plugin does not use it anymore, regular annotation instantiation feature is used (insertion of IrConstructorCall(annotationCtorSymbol)). - IR plugin still generates `SerialInfo$Impl` class to be compatible with previously compiled code & libraries. - Custom implementation over descriptors in IR plugin is removed; plugin now delegates to the existing lowering with some tweaks. - $Impl descriptor is removed from FE 1.0 because it is no longer needed for IR plugin; it shouldn't be exposed to users so FIR doesn't need it either. - Since language lowering is now used, it is possible to correctly instantiate SerialInfo annotations with default values even if they are from other modules and even if they were not processed by the plugin. #KT-48733 Fixed Fixes https://github.com/Kotlin/kotlinx.serialization/issues/1574 --- .../JvmAnnotationImplementationTransformer.kt | 357 +++++++++-------- .../build.gradle.kts | 1 + .../backend/ir/IrBuilderWithPluginContext.kt | 1 - .../ir/SerialInfoImplJvmIrGenerator.kt | 372 ++---------------- .../SerializationLoweringExtension.kt | 13 +- .../SerializationResolveExtension.kt | 9 +- .../resolve/KSerializerDescriptorResolver.kt | 53 --- .../testData/boxIr/serialInfo.kt | 32 ++ ...SerializationFirBlackBoxTestGenerated.java | 6 + .../SerializationIrBoxTestGenerated.java | 6 + 10 files changed, 286 insertions(+), 564 deletions(-) create mode 100644 plugins/kotlinx-serialization/testData/boxIr/serialInfo.kt diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt index 14e303c3133..35dc4121968 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.backend.jvm.lower +import org.jetbrains.kotlin.backend.common.ir.BuiltinSymbolsBase import org.jetbrains.kotlin.ir.deepCopyWithVariables import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase @@ -15,6 +16,7 @@ import org.jetbrains.kotlin.backend.jvm.ir.javaClassReference import org.jetbrains.kotlin.backend.jvm.unboxInlineClass import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* @@ -22,8 +24,11 @@ import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -41,6 +46,15 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, private val inInlineFunctionScope: Boolean get() = allScopes.any { it.irElement.safeAs()?.isInPublicInlineScope == true } + private val implementor = AnnotationPropertyImplementor( + jvmContext.irFactory, + jvmContext.irBuiltIns, + jvmContext.ir.symbols, + jvmContext.ir.symbols.javaLangClass, + jvmContext.ir.symbols.kClassJavaPropertyGetter.symbol, + ANNOTATION_IMPLEMENTATION + ) + @Suppress("UNUSED_PARAMETER") override fun chooseConstructor(implClass: IrClass, expression: IrConstructorCall) = implClass.constructors.single() @@ -53,88 +67,6 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, return super.visitConstructorCall(expression) } - private fun IrType.kClassToJClassIfNeeded(): IrType = when { - this.isKClass() -> jvmContext.ir.symbols.javaLangClass.starProjectedType - this.isKClassArray() -> jvmContext.irBuiltIns.arrayClass.typeWith( - jvmContext.ir.symbols.javaLangClass.starProjectedType - ) - else -> this - } - - private fun IrType.isKClassArray() = - this is IrSimpleType && isArray() && arguments.single().typeOrNull?.isKClass() == true - - private fun IrBuilderWithScope.kClassToJClass(irExpression: IrExpression): IrExpression = - irGet( - jvmContext.ir.symbols.javaLangClass.starProjectedType, - null, - jvmContext.ir.symbols.kClassJavaPropertyGetter.symbol - ).apply { - extensionReceiver = irExpression - } - - /** - * Copies array by one element, roughly as following: - * val size = kClassArray.size - * val result = arrayOfNulls(size) - * var i = 0 - * while(i < size) { - * result[i] = kClassArray[i].java - * i++ - * } - * Partially taken from ArrayConstructorLowering.kt - */ - private fun IrBuilderWithScope.kClassArrayToJClassArray(kClassArray: IrExpression): IrExpression { - val javaLangClass = jvmContext.ir.symbols.javaLangClass.starProjectedType - val jlcArray = jvmContext.ir.symbols.array.typeWith(javaLangClass) - val arrayClass = jvmContext.ir.symbols.array.owner - val arrayOfNulls = jvmContext.ir.symbols.arrayOfNulls - val arraySizeSymbol = arrayClass.findDeclaration { it.name.asString() == "size" }!!.getter!! - - val block = irBlock { - val sourceArray = createTmpVariable(kClassArray, "src", isMutable = false) - val index = createTmpVariable(irInt(0), "i", isMutable = true) - val size = createTmpVariable( - irCall(arraySizeSymbol).apply { dispatchReceiver = irGet(sourceArray) }, - "size", isMutable = false - ) - val result = createTmpVariable(irCall(arrayOfNulls, jlcArray).apply { - listOf(javaLangClass) - putValueArgument(0, irGet(size)) - }) - val comparison = primitiveOp2( - startOffset, endOffset, - context.irBuiltIns.lessFunByOperandType[context.irBuiltIns.intType.classifierOrFail]!!, - context.irBuiltIns.booleanType, - IrStatementOrigin.LT, - irGet(index), irGet(size) - ) - val setArraySymbol = arrayClass.functions.single { it.name == OperatorNameConventions.SET } - val getArraySymbol = arrayClass.functions.single { it.name == OperatorNameConventions.GET } - val inc = context.irBuiltIns.intType.getClass()!!.functions.single { it.name == OperatorNameConventions.INC } - +irWhile().also { loop -> - loop.condition = comparison - loop.body = irBlock { - val tempIndex = createTmpVariable(irGet(index)) - - val getArray = irCall(getArraySymbol).apply { - dispatchReceiver = irGet(sourceArray) - putValueArgument(0, irGet(tempIndex)) - } - +irCall(setArraySymbol).apply { - dispatchReceiver = irGet(result) - putValueArgument(0, irGet(tempIndex)) - putValueArgument(1, kClassToJClass(getArray)) - } - - +irSet(index.symbol, irCallOp(inc.symbol, index.type, irGet(index))) - } - } - +irGet(result) - } - return block - } - // There's no specialized Array.equals for unsigned arrays (as this is a Java function), so we force compiler not to box // result of property getter call override fun IrExpression.transformArrayEqualsArgument(type: IrType, irBuilder: IrBlockBodyBuilder): IrExpression = @@ -184,84 +116,12 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, annotationClass: IrClass, generatedConstructor: IrConstructor ) { - val ctorBodyBuilder = context.createIrBuilder(generatedConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET) - val ctorBody = context.irFactory.createBlockBody( - SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, listOf( - IrDelegatingConstructorCallImpl( - SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.irBuiltIns.unitType, context.irBuiltIns.anyClass.constructors.single(), - typeArgumentsCount = 0, valueArgumentsCount = 0 - ) - ) + implementor.implementAnnotationPropertiesAndConstructor( + annotationClass.getAnnotationProperties(), + implClass, + generatedConstructor, + this ) - - generatedConstructor.body = ctorBody - - annotationClass.getAnnotationProperties().forEach { property -> - val propType = property.getter!!.returnType - val storedFieldType = propType.kClassToJClassIfNeeded() - val propName = property.name - val field = context.irFactory.buildField { - startOffset = SYNTHETIC_OFFSET - endOffset = SYNTHETIC_OFFSET - name = propName - type = storedFieldType - origin = ANNOTATION_IMPLEMENTATION - isFinal = true - visibility = DescriptorVisibilities.PRIVATE - }.also { it.parent = implClass } - - val parameter = generatedConstructor.addValueParameter(propName.asString(), propType) - - val defaultExpression = property.backingField?.initializer?.expression - val newDefaultValue: IrExpressionBody? = - if (defaultExpression is IrGetValue && defaultExpression.symbol.owner is IrValueParameter) { - // INITIALIZE_PROPERTY_FROM_PARAMETER - (defaultExpression.symbol.owner as IrValueParameter).defaultValue - } else if (defaultExpression != null) { - property.backingField!!.initializer - } else null - parameter.defaultValue = newDefaultValue?.deepCopyWithVariables()?.also { it.transformChildrenVoid() } - - ctorBody.statements += with(ctorBodyBuilder) { - val param = irGet(parameter) - val fieldValue = when { - propType.isKClass() -> kClassToJClass(param) - propType.isKClassArray() -> kClassArrayToJClassArray(param) - else -> param - } - irSetField(irGet(implClass.thisReceiver!!), field, fieldValue) - } - - val prop = implClass.addProperty { - startOffset = SYNTHETIC_OFFSET - endOffset = SYNTHETIC_OFFSET - name = propName - isVar = false - origin = ANNOTATION_IMPLEMENTATION - }.apply { - field.correspondingPropertySymbol = this.symbol - backingField = field - parent = implClass - overriddenSymbols = listOf(property.symbol) - } - - prop.addGetter { - startOffset = SYNTHETIC_OFFSET - endOffset = SYNTHETIC_OFFSET - name = propName // Annotation value getter should be named 'x', not 'getX' - returnType = propType.kClassToJClassIfNeeded() // On JVM, annotation store j.l.Class even if declared with KClass - origin = ANNOTATION_IMPLEMENTATION - visibility = DescriptorVisibilities.PUBLIC - modality = Modality.FINAL - }.apply { - correspondingPropertySymbol = prop.symbol - dispatchReceiverParameter = implClass.thisReceiver!!.copyTo(this) - body = context.createIrBuilder(symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { - +irReturn(irGetField(irGet(dispatchReceiverParameter!!), field)) - } - overriddenSymbols = listOf(property.getter!!.symbol) - } - } } override fun generateFunctionBodies( @@ -279,4 +139,183 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, generator.generateToStringMethod(toStringFun, implProperties) } + class AnnotationPropertyImplementor( + val irFactory: IrFactory, + val irBuiltIns: IrBuiltIns, + val symbols: BuiltinSymbolsBase, + val javaLangClassSymbol: IrClassSymbol, + val kClassJavaPropertyGetterSymbol: IrSimpleFunctionSymbol, + val originForProp: IrDeclarationOrigin + ) { + + /** + * Copies array by one element, roughly as following: + * val size = kClassArray.size + * val result = arrayOfNulls(size) + * var i = 0 + * while(i < size) { + * result[i] = kClassArray[i].java + * i++ + * } + * Partially taken from ArrayConstructorLowering.kt + */ + private fun IrBuilderWithScope.kClassArrayToJClassArray(kClassArray: IrExpression): IrExpression { + val javaLangClassType = javaLangClassSymbol.starProjectedType + val jlcArray = symbols.array.typeWith(javaLangClassType) + val arrayClass = symbols.array.owner + val arrayOfNulls = symbols.arrayOfNulls + val arraySizeSymbol = arrayClass.findDeclaration { it.name.asString() == "size" }!!.getter!! + + val block = irBlock { + val sourceArray = createTmpVariable(kClassArray, "src", isMutable = false) + val index = createTmpVariable(irInt(0), "i", isMutable = true) + val size = createTmpVariable( + irCall(arraySizeSymbol).apply { dispatchReceiver = irGet(sourceArray) }, + "size", isMutable = false + ) + val result = createTmpVariable(irCall(arrayOfNulls, jlcArray).apply { + listOf(javaLangClassType) + putValueArgument(0, irGet(size)) + }) + val comparison = primitiveOp2( + startOffset, endOffset, + context.irBuiltIns.lessFunByOperandType[context.irBuiltIns.intType.classifierOrFail]!!, + context.irBuiltIns.booleanType, + IrStatementOrigin.LT, + irGet(index), irGet(size) + ) + val setArraySymbol = arrayClass.functions.single { it.name == OperatorNameConventions.SET } + val getArraySymbol = arrayClass.functions.single { it.name == OperatorNameConventions.GET } + val inc = context.irBuiltIns.intType.getClass()!!.functions.single { it.name == OperatorNameConventions.INC } + +irWhile().also { loop -> + loop.condition = comparison + loop.body = irBlock { + val tempIndex = createTmpVariable(irGet(index)) + + val getArray = irCall(getArraySymbol).apply { + dispatchReceiver = irGet(sourceArray) + putValueArgument(0, irGet(tempIndex)) + } + +irCall(setArraySymbol).apply { + dispatchReceiver = irGet(result) + putValueArgument(0, irGet(tempIndex)) + putValueArgument(1, kClassToJClass(getArray)) + } + + +irSet(index.symbol, irCallOp(inc.symbol, index.type, irGet(index))) + } + } + +irGet(result) + } + return block + } + + private fun IrType.kClassToJClassIfNeeded(): IrType = when { + this.isKClass() -> javaLangClassSymbol.starProjectedType + this.isKClassArray() -> symbols.array.typeWith( + javaLangClassSymbol.starProjectedType + ) + + else -> this + } + + private fun IrType.isKClassArray() = + this is IrSimpleType && isArray() && arguments.single().typeOrNull?.isKClass() == true + + private fun IrBuilderWithScope.kClassToJClass(irExpression: IrExpression): IrExpression = + irGet( + javaLangClassSymbol.starProjectedType, + null, + kClassJavaPropertyGetterSymbol + ).apply { + extensionReceiver = irExpression + } + + fun implementAnnotationPropertiesAndConstructor( + annotationProperties: List, + implClass: IrClass, + generatedConstructor: IrConstructor, + defaultValueTransformer: IrElementTransformerVoid? + ) { + val ctorBodyBuilder = irBuiltIns.createIrBuilder(generatedConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET) + val ctorBody = irFactory.createBlockBody( + SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, listOf( + IrDelegatingConstructorCallImpl( + SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, irBuiltIns.unitType, irBuiltIns.anyClass.constructors.single(), + typeArgumentsCount = 0, valueArgumentsCount = 0 + ) + ) + ) + + generatedConstructor.body = ctorBody + + annotationProperties.forEach { property -> + val propType = property.getter!!.returnType + val storedFieldType = propType.kClassToJClassIfNeeded() + val propName = property.name + val field = irFactory.buildField { + startOffset = SYNTHETIC_OFFSET + endOffset = SYNTHETIC_OFFSET + name = propName + type = storedFieldType + origin = originForProp + isFinal = true + visibility = DescriptorVisibilities.PRIVATE + }.also { it.parent = implClass } + + val parameter = generatedConstructor.addValueParameter(propName.asString(), propType) + + val defaultExpression = property.backingField?.initializer?.expression + val newDefaultValue: IrExpressionBody? = + if (defaultExpression is IrGetValue && defaultExpression.symbol.owner is IrValueParameter) { + // INITIALIZE_PROPERTY_FROM_PARAMETER + (defaultExpression.symbol.owner as IrValueParameter).defaultValue + } else if (defaultExpression != null) { + property.backingField!!.initializer + } else null + parameter.defaultValue = newDefaultValue?.deepCopyWithVariables() + ?.also { if (defaultValueTransformer != null) it.transformChildrenVoid(defaultValueTransformer) } + + ctorBody.statements += with(ctorBodyBuilder) { + val param = irGet(parameter) + val fieldValue = when { + propType.isKClass() -> kClassToJClass(param) + propType.isKClassArray() -> kClassArrayToJClassArray(param) + else -> param + } + irSetField(irGet(implClass.thisReceiver!!), field, fieldValue) + } + + val prop = implClass.addProperty { + startOffset = SYNTHETIC_OFFSET + endOffset = SYNTHETIC_OFFSET + name = propName + isVar = false + origin = originForProp + }.apply { + field.correspondingPropertySymbol = this.symbol + backingField = field + parent = implClass + overriddenSymbols = listOf(property.symbol) + } + + prop.addGetter { + startOffset = SYNTHETIC_OFFSET + endOffset = SYNTHETIC_OFFSET + name = propName // Annotation value getter should be named 'x', not 'getX' + returnType = propType.kClassToJClassIfNeeded() // On JVM, annotation store j.l.Class even if declared with KClass + origin = originForProp + visibility = DescriptorVisibilities.PUBLIC + modality = Modality.FINAL + }.apply { + correspondingPropertySymbol = prop.symbol + dispatchReceiverParameter = implClass.thisReceiver!!.copyTo(this) + body = irBuiltIns.createIrBuilder(symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { + +irReturn(irGetField(irGet(dispatchReceiverParameter!!), field)) + } + overriddenSymbols = listOf(property.getter!!.symbol) + } + } + } + } } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/build.gradle.kts b/plugins/kotlinx-serialization/kotlinx-serialization.backend/build.gradle.kts index 746bcb416c0..601af3bd6bd 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/build.gradle.kts +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/build.gradle.kts @@ -10,6 +10,7 @@ dependencies { compileOnly(project(":compiler:ir.backend.common")) compileOnly(project(":compiler:backend.jvm")) compileOnly(project(":compiler:backend.jvm.codegen")) + compileOnly(project(":compiler:backend.jvm.lower")) compileOnly(project(":compiler:ir.tree")) compileOnly(project(":js:js.frontend")) compileOnly(project(":js:js.translator")) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt index 9d56e20864b..6f9c1aed0e2 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt @@ -366,7 +366,6 @@ interface IrBuilderWithPluginContext { annotations.mapNotNull { annotationCall -> val annotationClass = annotationCall.symbol.owner.parentAsClass if (!annotationClass.isSerialInfoAnnotation) return@mapNotNull null - annotationCall.deepCopyWithVariables() } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt index f7cf52e22b2..b0ca0263583 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt @@ -5,138 +5,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.backend.jvm.lower.JvmAnnotationImplementationTransformer import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.declarations.* 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.* -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.kotlin.types.KotlinType import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames -// This doesn't support annotation arguments of type KClass and Array because the codegen doesn't compute JVM signatures for -// such cases correctly (because inheriting from annotation classes is prohibited in Kotlin). -// Currently it results in an "accidental override" error where a method with return type KClass conflicts with the one with Class. -@OptIn(ObsoleteDescriptorBasedAPI::class) class SerialInfoImplJvmIrGenerator( private val context: SerializationPluginContext, private val moduleFragment: IrModuleFragment, -) : IrBuilderWithPluginContext { - override val compilerContext: SerializationPluginContext - get() = context - - private val jvmNameClass get() = context.referenceClass(ClassId.topLevel(DescriptorUtils.JVM_NAME))!!.owner - +) { private val javaLangClass = createClass(createPackage("java.lang"), "Class", ClassKind.CLASS) - private val javaLangType = javaLangClass.starProjectedType - - private val implGenerated = mutableSetOf() - private val annotationToImpl = mutableMapOf() - - fun getImplClass(serialInfoAnnotationClass: IrClass): IrClass = - annotationToImpl.getOrPut(serialInfoAnnotationClass) { - @OptIn(FirIncompatiblePluginAPI::class) - val implClassSymbol = context.referenceClass(serialInfoAnnotationClass.kotlinFqName.child(SerialEntityNames.IMPL_NAME)) - implClassSymbol!!.owner.apply(this::generate) - } - - fun generate(irClass: IrClass) { - if (!implGenerated.add(irClass)) return - - val properties = irClass.declarations.filterIsInstance() - if (properties.isEmpty()) return - - val startOffset = UNDEFINED_OFFSET - val endOffset = UNDEFINED_OFFSET - - val ctor = irClass.addConstructor { - visibility = DescriptorVisibilities.PUBLIC - } - val ctorBody = context.irFactory.createBlockBody( - startOffset, endOffset, listOf( - IrDelegatingConstructorCallImpl( - startOffset, endOffset, context.irBuiltIns.unitType, context.irBuiltIns.anyClass.constructors.single(), - typeArgumentsCount = 0, valueArgumentsCount = 0 - ) - ) - ) - ctor.body = ctorBody - - for (property in properties) { - generateSimplePropertyWithBackingField(property.descriptor, irClass, Name.identifier("_" + property.name.asString())) - - val getter = property.getter!! - getter.origin = SERIALIZATION_PLUGIN_ORIGIN - // Add JvmName annotation to property getters to force the resulting JVM method name for 'x' be 'x', instead of 'getX', - // and to avoid having useless bridges for it generated in BridgeLowering. - // Unfortunately, this results in an extra `@JvmName` annotation in the bytecode, but it shouldn't matter very much. - getter.annotations += jvmName(property.name.asString()) - - val field = property.backingField!! - field.visibility = DescriptorVisibilities.PRIVATE - field.origin = SERIALIZATION_PLUGIN_ORIGIN - - val parameter = ctor.addValueParameter(property.name.asString(), field.type) - ctorBody.statements += IrSetFieldImpl( - startOffset, endOffset, field.symbol, - IrGetValueImpl(startOffset, endOffset, irClass.thisReceiver!!.symbol), - IrGetValueImpl(startOffset, endOffset, parameter.symbol), - context.irBuiltIns.unitType, - ) - } - } - - private fun jvmName(name: String): IrConstructorCall = - IrConstructorCallImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, jvmNameClass.defaultType, jvmNameClass.constructors.single().symbol, - typeArgumentsCount = 0, constructorTypeArgumentsCount = 0, valueArgumentsCount = 1, - ).apply { - putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name)) - } - - @FirIncompatiblePluginAPI - fun KotlinType.toIrType() = compilerContext.typeTranslator.translateType(this) - - private fun IrType.kClassToJClassIfNeeded(): IrType = when { - this.isKClass() -> javaLangType - this.isKClassArray() -> compilerContext.irBuiltIns.arrayClass.typeWith(javaLangType) - else -> this - } - - private fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression { - val getterSymbol = kClassJava.owner.getter!!.symbol - return IrCallImpl( - startOffset, endOffset, - javaLangClass.starProjectedType, - getterSymbol, - typeArgumentsCount = getterSymbol.owner.typeParameters.size, - valueArgumentsCount = 0, - origin = IrStatementOrigin.GET_PROPERTY - ).apply { - this.extensionReceiver = irExpression - } - } private val jvmName: IrClassSymbol = createClass(createPackage("kotlin.jvm"), "JvmName", ClassKind.ANNOTATION_CLASS) { klass -> klass.addConstructor().apply { @@ -168,8 +59,40 @@ class SerialInfoImplJvmIrGenerator( } }.symbol - private fun IrType.isKClassArray() = - this is IrSimpleType && isArray() && arguments.single().typeOrNull?.isKClass() == true + private val implementor = JvmAnnotationImplementationTransformer.AnnotationPropertyImplementor( + context.irFactory, + context.irBuiltIns, + context.symbols, + javaLangClass, + kClassJava.owner.getter!!.symbol, + SERIALIZATION_PLUGIN_ORIGIN + ) + + fun generateImplementationFor(annotationClass: IrClass) { + + val properties = annotationClass.declarations.filterIsInstance() + + val subclass = context.irFactory.buildClass { + startOffset = UNDEFINED_OFFSET + endOffset = UNDEFINED_OFFSET + name = SerialEntityNames.IMPL_NAME + origin = SERIALIZATION_PLUGIN_ORIGIN + visibility = DescriptorVisibilities.PUBLIC + }.apply { + parent = annotationClass + createImplicitParameterDeclarationWithWrappedDescriptor() + superTypes = listOf(annotationClass.defaultType) + } + annotationClass.declarations.add(subclass) + + val ctor = subclass.addConstructor { + startOffset = UNDEFINED_OFFSET + endOffset = UNDEFINED_OFFSET + visibility = DescriptorVisibilities.PUBLIC + } + + implementor.implementAnnotationPropertiesAndConstructor(properties, subclass, ctor, null) + } private fun createPackage(packageName: String): IrPackageFragment = IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment( @@ -191,227 +114,4 @@ 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, - SERIALIZATION_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, - SERIALIZATION_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, - SERIALIZATION_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, SERIALIZATION_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, - SERIALIZATION_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/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt index 6872e3ba861..d386801abbd 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt @@ -17,7 +17,6 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.ir.fileParent import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid @@ -29,7 +28,6 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlinx.serialization.compiler.backend.ir.* import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializationJvmIrIntrinsicSupport -import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages import java.util.concurrent.ConcurrentHashMap @@ -53,7 +51,6 @@ fun ClassLoweringPass.runOnFileInOrder(irFile: IrFile) { class SerializationPluginContext(baseContext: IrPluginContext, val metadataPlugin: SerializationDescriptorSerializerPlugin?) : IrPluginContext by baseContext, SerializationBaseContext { - lateinit var serialInfoImplJvmIrGenerator: SerialInfoImplJvmIrGenerator internal val copiedStaticWriteSelf: MutableMap = ConcurrentHashMap() @@ -95,8 +92,9 @@ private class SerializerClassLowering( moduleFragment: IrModuleFragment ) : IrElementTransformerVoid(), ClassLoweringPass { val context: SerializationPluginContext = SerializationPluginContext(baseContext, metadataPlugin) - private val serialInfoJvmGenerator = - SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it } + + // Lazy to avoid creating generator in non-JVM backends + private val serialInfoJvmGenerator by lazy(LazyThreadSafetyMode.NONE) { SerialInfoImplJvmIrGenerator(context, moduleFragment) } override fun lower(irClass: IrClass) { irClass.runPluginSafe { @@ -104,9 +102,8 @@ private class SerializerClassLowering( SerializerIrGenerator.generate(irClass, context, context.metadataPlugin) SerializableCompanionIrGenerator.generate(irClass, context) - @OptIn(ObsoleteDescriptorBasedAPI::class) - if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) { - serialInfoJvmGenerator.generate(irClass) + if (context.platform.isJvm() && irClass.isSerialInfoAnnotation) { + serialInfoJvmGenerator.generateImplementationFor(irClass) } } } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt index f4c6966e024..a6c85042854 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt @@ -24,14 +24,13 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.* open class SerializationResolveExtension @JvmOverloads constructor(val metadataPlugin: SerializationDescriptorSerializerPlugin? = null) : SyntheticResolveExtension { override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List = when { - thisDescriptor.isSerialInfoAnnotation && thisDescriptor.platform?.isJvm() == true -> listOf(SerialEntityNames.IMPL_NAME) (thisDescriptor.shouldHaveGeneratedSerializer) && !thisDescriptor.hasCompanionObjectAsSerializer -> listOf(SerialEntityNames.SERIALIZER_CLASS_NAME) else -> listOf() } override fun getPossibleSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List? { - return listOf(SerialEntityNames.IMPL_NAME, SerialEntityNames.SERIALIZER_CLASS_NAME) + return listOf(SerialEntityNames.SERIALIZER_CLASS_NAME) } override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List = when { @@ -73,9 +72,7 @@ open class SerializationResolveExtension @JvmOverloads constructor(val metadataP declarationProvider: ClassMemberDeclarationProvider, result: MutableSet ) { - if (thisDescriptor.isSerialInfoAnnotation && name == SerialEntityNames.IMPL_NAME) - result.add(KSerializerDescriptorResolver.addSerialInfoImplClass(thisDescriptor, declarationProvider, ctx)) - else if (thisDescriptor.shouldHaveGeneratedSerializer && name == SerialEntityNames.SERIALIZER_CLASS_NAME && + if (thisDescriptor.shouldHaveGeneratedSerializer && name == SerialEntityNames.SERIALIZER_CLASS_NAME && result.none { it.name == SerialEntityNames.SERIALIZER_CLASS_NAME } ) result.add(KSerializerDescriptorResolver.addSerializerImplClass(thisDescriptor, declarationProvider, ctx)) @@ -88,7 +85,6 @@ open class SerializationResolveExtension @JvmOverloads constructor(val metadataP else null override fun addSyntheticSupertypes(thisDescriptor: ClassDescriptor, supertypes: MutableList) { - KSerializerDescriptorResolver.addSerialInfoSuperType(thisDescriptor, supertypes) KSerializerDescriptorResolver.addSerializerSupertypes(thisDescriptor, supertypes) KSerializerDescriptorResolver.addSerializerFactorySuperType(thisDescriptor, supertypes) } @@ -125,7 +121,6 @@ open class SerializationResolveExtension @JvmOverloads constructor(val metadataP fromSupertypes: ArrayList, result: MutableSet ) { - KSerializerDescriptorResolver.generateDescriptorsForAnnotationImpl(thisDescriptor, fromSupertypes, result) KSerializerDescriptorResolver.generateSerializerProperties(thisDescriptor, fromSupertypes, name, result) } } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt index c23bc436967..adb1eb48925 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt @@ -47,12 +47,6 @@ object KSerializerDescriptorResolver { && (thisDescriptor.containingDeclaration as ClassDescriptor).isSerialInfoAnnotation } - fun addSerialInfoSuperType(thisDescriptor: ClassDescriptor, supertypes: MutableList) { - if (isSerialInfoImpl(thisDescriptor)) { - supertypes.add((thisDescriptor.containingDeclaration as LazyClassDescriptor).toSimpleType(false)) - } - } - fun addSerializerFactorySuperType(classDescriptor: ClassDescriptor, supertypes: MutableList) { if (!classDescriptor.needSerializerFactory()) return val serializerFactoryClass = @@ -72,36 +66,6 @@ object KSerializerDescriptorResolver { supertypes.add(classDescriptor.createSerializerTypeFor(serializableClassDescriptor.defaultType, fqName)) } - fun addSerialInfoImplClass( - interfaceDesc: ClassDescriptor, - declarationProvider: ClassMemberDeclarationProvider, - ctx: LazyClassContext - ): ClassDescriptor { - val interfaceDecl = declarationProvider.correspondingClassOrObject!! - val scope = ctx.declarationScopeProvider.getResolutionScopeForDeclaration(declarationProvider.ownerInfo!!.scopeAnchor) - - val props = interfaceDecl.primaryConstructorParameters - // if there is some properties, there will be a public synthetic constructor at the codegen phase - val primaryCtorVisibility = if (props.isEmpty()) DescriptorVisibilities.PUBLIC else DescriptorVisibilities.PRIVATE - - val descriptor = SyntheticClassOrObjectDescriptor( - ctx, - interfaceDecl, - interfaceDesc, - IMPL_NAME, - interfaceDesc.source, - scope, - Modality.FINAL, - DescriptorVisibilities.PUBLIC, - Annotations.create(listOf(createDeprecatedHiddenAnnotation(interfaceDesc.module))), - primaryCtorVisibility, - ClassKind.CLASS, - false - ) - descriptor.initialize() - return descriptor - } - fun addSerializerImplClass( thisDescriptor: ClassDescriptor, declarationProvider: ClassMemberDeclarationProvider, @@ -628,23 +592,6 @@ object KSerializerDescriptorResolver { return f } - fun generateDescriptorsForAnnotationImpl( - thisDescriptor: ClassDescriptor, - fromSupertypes: List, - result: MutableCollection - ) { - if (isSerialInfoImpl(thisDescriptor)) { - result.add( - fromSupertypes.first().newCopyBuilder().apply { - setOwner(thisDescriptor) - setModality(Modality.FINAL) - setKind(CallableMemberDescriptor.Kind.SYNTHESIZED) - setDispatchReceiverParameter(thisDescriptor.thisAsReceiverParameter) - }.build()!! - ) - } - } - // create properties typeSerial0, typeSerial1, etc... for storing generic arguments' serializers private fun createLocalSerializersFieldsDescriptor( name: Name, diff --git a/plugins/kotlinx-serialization/testData/boxIr/serialInfo.kt b/plugins/kotlinx-serialization/testData/boxIr/serialInfo.kt new file mode 100644 index 00000000000..61c890c2ee5 --- /dev/null +++ b/plugins/kotlinx-serialization/testData/boxIr/serialInfo.kt @@ -0,0 +1,32 @@ +// WITH_STDLIB + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.descriptors.* + +@SerialInfo +@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY) +annotation class MyId(val id: Int, val type: String = "foo") + +@Serializable +@MyId(10) +class Foo(@MyId(20) val i: Int) + +fun box(): String { + val desc = Foo.serializer().descriptor + val classId = desc.annotations.filterIsInstance().single() + val propId = desc.getElementAnnotations(0).filterIsInstance().single().id + if (classId.id != 10) return "Incorrect class annotation: ${classId}" + if (classId.type != "foo") return "Incorrect default argument: ${classId}" + if (!classId::class.java.toString().contains("annotationImpl")) return "Backend doesn't use annotation instantiation: ${classId::class}" + if (propId != 20) return "Incorrect propery annotation: $propId" + val implClassJava = Class.forName("MyId\$Impl") + if (implClassJava.toString() != "class MyId\$Impl") return "Old annotation implementations are not preserved for compatibility" + val ctorStr = implClassJava.constructors.toList().toString() + if (!ctorStr.contains("public MyId\$Impl(int,java.lang.String)")) return "Compatibility impl does not contain correct constructor: $ctorStr" + val methodsStr = implClassJava.methods.toList().toString() + if (!methodsStr.contains("public final int MyId\$Impl.id()")) return "Compatibility impl does not contain correct methods: $methodsStr" + if (!methodsStr.contains("public final java.lang.String MyId\$Impl.type()")) return "Compatibility impl does not contain correct methods: $methodsStr" + return "OK" +} \ No newline at end of file diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java index c12c1f1d0b9..578143a1033 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java @@ -105,6 +105,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization runTest("plugins/kotlinx-serialization/testData/boxIr/sealedInterfaces.kt"); } + @Test + @TestMetadata("serialInfo.kt") + public void testSerialInfo() throws Exception { + runTest("plugins/kotlinx-serialization/testData/boxIr/serialInfo.kt"); + } + @Test @TestMetadata("serializableOnPropertyType.kt") public void testSerializableOnPropertyType() throws Exception { diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java index fec91fabe89..1640cbfaf67 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java @@ -103,6 +103,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT runTest("plugins/kotlinx-serialization/testData/boxIr/sealedInterfaces.kt"); } + @Test + @TestMetadata("serialInfo.kt") + public void testSerialInfo() throws Exception { + runTest("plugins/kotlinx-serialization/testData/boxIr/serialInfo.kt"); + } + @Test @TestMetadata("serializableOnPropertyType.kt") public void testSerializableOnPropertyType() throws Exception {