From 42aef01d79fe3760d8117ba1b5196956351a8b59 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 28 Oct 2020 13:47:45 +0100 Subject: [PATCH] Kx-serialization: support SerialInfo annotations from dependencies in JVM IR Also handle call sites correctly, by generating an instance of the Impl class instead of trying to create the annotation. #KT-42976 Fixed --- .../compiler/backend/ir/GeneratorHelpers.kt | 6 +-- .../ir/SerialInfoImplJvmIrGenerator.kt | 26 ++++++----- .../backend/ir/SerializerForEnumsGenerator.kt | 7 ++- .../backend/ir/SerializerIrGenerator.kt | 45 ++++++++++++------- .../SerializationLoweringExtension.kt | 12 ++--- 5 files changed, 56 insertions(+), 40 deletions(-) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt index fe117dc643b..2a823f553fd 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt @@ -228,7 +228,7 @@ interface IrBuilderExtension { val irPropertySymbol = compilerContext.symbolTable.referenceProperty(propertyDescriptor) assert(irPropertySymbol.isBound || declare) - if (declare) { + if (!irPropertySymbol.isBound) { with(propertyDescriptor) { propertyParent.factory.createProperty( propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, irPropertySymbol, @@ -270,7 +270,7 @@ interface IrBuilderExtension { } } - fun generatePropertyAccessor( + private fun generatePropertyAccessor( property: IrProperty, descriptor: PropertyAccessorDescriptor, fieldSymbol: IrFieldSymbol, @@ -279,7 +279,7 @@ interface IrBuilderExtension { val symbol = compilerContext.symbolTable.referenceSimpleFunction(descriptor) assert(symbol.isBound || declare) - if (declare) { + if (!symbol.isBound) { with(descriptor) { property.factory.createFunction( fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, symbol, diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt index 3fec3b27092..a6261998af8 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt @@ -15,17 +15,17 @@ import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.util.kotlinFqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext -import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +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. // TODO: support annotation properties of types KClass<...> and Array>. class SerialInfoImplJvmIrGenerator( - private val irClass: IrClass, private val context: SerializationPluginContext, ) : IrBuilderExtension { override val compilerContext: SerializationPluginContext @@ -33,7 +33,18 @@ class SerialInfoImplJvmIrGenerator( private val jvmNameClass = context.referenceClass(DescriptorUtils.JVM_NAME)!!.owner - private fun generate() { + private val implGenerated = mutableSetOf() + private val annotationToImpl = mutableMapOf() + + fun getImplClass(serialInfoAnnotationClass: IrClass): IrClass = + annotationToImpl.getOrPut(serialInfoAnnotationClass) { + 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 @@ -54,7 +65,7 @@ class SerialInfoImplJvmIrGenerator( ctor.body = ctorBody for (property in properties) { - generateSimplePropertyWithBackingField(property.descriptor, irClass, false, Name.identifier("_" + property.name.asString())) + generateSimplePropertyWithBackingField(property.descriptor, irClass, true, Name.identifier("_" + property.name.asString())) val getter = property.getter!! getter.origin = SERIALIZABLE_SYNTHETIC_ORIGIN @@ -84,11 +95,4 @@ class SerialInfoImplJvmIrGenerator( ).apply { putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name)) } - - companion object { - fun generate(irClass: IrClass, context: SerializationPluginContext) { - if (KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) - SerialInfoImplJvmIrGenerator(irClass, context).generate() - } - } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt index c5471b75d7d..3cfc07836b6 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt @@ -27,10 +27,9 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.* class SerializerForEnumsGenerator( irClass: IrClass, compilerContext: SerializationPluginContext, - bindingContext: BindingContext -) : - SerializerIrGenerator(irClass, compilerContext, bindingContext, null) { - + bindingContext: BindingContext, + serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, +) : SerializerIrGenerator(irClass, compilerContext, bindingContext, null, serialInfoJvmGenerator) { override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc -> fun irThis(): IrExpression = IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index 5428fd0c0ed..dcbf0f23398 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -14,18 +14,14 @@ import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.mapValueParametersIndexed import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.constructors -import org.jetbrains.kotlin.ir.util.patchDeclarationParents -import org.jetbrains.kotlin.ir.util.withReferenceScope +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.util.OperatorNameConventions @@ -50,10 +46,9 @@ open class SerializerIrGenerator( val irClass: IrClass, final override val compilerContext: SerializationPluginContext, bindingContext: BindingContext, - metadataPlugin: SerializationDescriptorSerializerPlugin? -) : - SerializerCodegen(irClass.descriptor, bindingContext, metadataPlugin), IrBuilderExtension { - + metadataPlugin: SerializationDescriptorSerializerPlugin?, + private val serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, +) : SerializerCodegen(irClass.descriptor, bindingContext, metadataPlugin), IrBuilderExtension { protected val serializableIrClass = compilerContext.symbolTable.referenceClass(serializableDescriptor).owner protected val irAnySerialDescProperty = anySerialDescProperty?.let { compilerContext.symbolTable.referenceProperty(it) } @@ -160,9 +155,24 @@ open class SerializerIrGenerator( receiver: IrVariable, method: IrFunctionSymbol ) { - annotations.forEach { annotationCall -> - if ((annotationCall.symbol.descriptor as? ClassConstructorDescriptor)?.constructedClass?.isSerialInfoAnnotation == true) - +irInvoke(irGet(receiver), method, annotationCall.deepCopyWithVariables()) + for (annotationCall in annotations) { + val annotationClass = annotationCall.symbol.owner.parentAsClass + if (!annotationClass.descriptor.isSerialInfoAnnotation) continue + + val createAnnotation = if (compilerContext.platform.isJvm()) { + val implClass = serialInfoJvmGenerator.getImplClass(annotationClass) + val ctor = implClass.constructors.singleOrNull { it.valueParameters.size == annotationCall.valueArgumentsCount } + ?: error("No constructor args found for SerialInfo annotation Impl class: ${implClass.render()}") + irCall(ctor).apply { + for (i in 0 until annotationCall.valueArgumentsCount) { + putValueArgument(i, annotationCall.getValueArgument(i)!!.deepCopyWithVariables()) + } + } + } else { + annotationCall.deepCopyWithVariables() + } + + +irInvoke(irGet(receiver), method, createAnnotation) } } @@ -496,13 +506,14 @@ open class SerializerIrGenerator( irClass: IrClass, context: SerializationPluginContext, bindingContext: BindingContext, - metadataPlugin: SerializationDescriptorSerializerPlugin? + metadataPlugin: SerializationDescriptorSerializerPlugin?, + serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, ) { val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return if (serializableDesc.isSerializableEnum()) { - SerializerForEnumsGenerator(irClass, context, bindingContext).generate() + SerializerForEnumsGenerator(irClass, context, bindingContext, serialInfoJvmGenerator).generate() } else { - SerializerIrGenerator(irClass, context, bindingContext, metadataPlugin).generate() + SerializerIrGenerator(irClass, context, bindingContext, metadataPlugin, serialInfoJvmGenerator).generate() } irClass.patchDeclarationParents(irClass.parent) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt index 89f3b8e3025..909bc9342b8 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerialInfoImplJvm import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableCompanionIrGenerator import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableIrGenerator import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver /** * Copy of [runOnFilePostfix], but this implementation first lowers declaration, then its children. @@ -44,15 +45,16 @@ typealias SerializationPluginContext = IrPluginContext private class SerializerClassLowering( val context: SerializationPluginContext, val metadataPlugin: SerializationDescriptorSerializerPlugin? -) : - IrElementTransformerVoid(), ClassLoweringPass { +) : IrElementTransformerVoid(), ClassLoweringPass { + private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context) + override fun lower(irClass: IrClass) { SerializableIrGenerator.generate(irClass, context, context.bindingContext) - SerializerIrGenerator.generate(irClass, context, context.bindingContext, metadataPlugin) + SerializerIrGenerator.generate(irClass, context, context.bindingContext, metadataPlugin, serialInfoJvmGenerator) SerializableCompanionIrGenerator.generate(irClass, context, context.bindingContext) - if (context.platform.isJvm()) { - SerialInfoImplJvmIrGenerator.generate(irClass, context) + if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) { + serialInfoJvmGenerator.generate(irClass) } } }