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 {