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
This commit is contained in:
Alexander Udalov
2020-10-28 13:47:45 +01:00
parent 51ded98c4b
commit 42aef01d79
5 changed files with 56 additions and 40 deletions
@@ -228,7 +228,7 @@ interface IrBuilderExtension {
val irPropertySymbol = compilerContext.symbolTable.referenceProperty(propertyDescriptor) val irPropertySymbol = compilerContext.symbolTable.referenceProperty(propertyDescriptor)
assert(irPropertySymbol.isBound || declare) assert(irPropertySymbol.isBound || declare)
if (declare) { if (!irPropertySymbol.isBound) {
with(propertyDescriptor) { with(propertyDescriptor) {
propertyParent.factory.createProperty( propertyParent.factory.createProperty(
propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, irPropertySymbol, propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, irPropertySymbol,
@@ -270,7 +270,7 @@ interface IrBuilderExtension {
} }
} }
fun generatePropertyAccessor( private fun generatePropertyAccessor(
property: IrProperty, property: IrProperty,
descriptor: PropertyAccessorDescriptor, descriptor: PropertyAccessorDescriptor,
fieldSymbol: IrFieldSymbol, fieldSymbol: IrFieldSymbol,
@@ -279,7 +279,7 @@ interface IrBuilderExtension {
val symbol = compilerContext.symbolTable.referenceSimpleFunction(descriptor) val symbol = compilerContext.symbolTable.referenceSimpleFunction(descriptor)
assert(symbol.isBound || declare) assert(symbol.isBound || declare)
if (declare) { if (!symbol.isBound) {
with(descriptor) { with(descriptor) {
property.factory.createFunction( property.factory.createFunction(
fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, symbol, fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, symbol,
@@ -15,17 +15,17 @@ import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext 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<KClass> because the codegen doesn't compute JVM signatures for // This doesn't support annotation arguments of type KClass and Array<KClass> because the codegen doesn't compute JVM signatures for
// such cases correctly (because inheriting from annotation classes is prohibited in Kotlin). // 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. // 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<KClass<...>>. // TODO: support annotation properties of types KClass<...> and Array<KClass<...>>.
class SerialInfoImplJvmIrGenerator( class SerialInfoImplJvmIrGenerator(
private val irClass: IrClass,
private val context: SerializationPluginContext, private val context: SerializationPluginContext,
) : IrBuilderExtension { ) : IrBuilderExtension {
override val compilerContext: SerializationPluginContext override val compilerContext: SerializationPluginContext
@@ -33,7 +33,18 @@ class SerialInfoImplJvmIrGenerator(
private val jvmNameClass = context.referenceClass(DescriptorUtils.JVM_NAME)!!.owner private val jvmNameClass = context.referenceClass(DescriptorUtils.JVM_NAME)!!.owner
private fun generate() { private val implGenerated = mutableSetOf<IrClass>()
private val annotationToImpl = mutableMapOf<IrClass, IrClass>()
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<IrProperty>() val properties = irClass.declarations.filterIsInstance<IrProperty>()
if (properties.isEmpty()) return if (properties.isEmpty()) return
@@ -54,7 +65,7 @@ class SerialInfoImplJvmIrGenerator(
ctor.body = ctorBody ctor.body = ctorBody
for (property in properties) { 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!! val getter = property.getter!!
getter.origin = SERIALIZABLE_SYNTHETIC_ORIGIN getter.origin = SERIALIZABLE_SYNTHETIC_ORIGIN
@@ -84,11 +95,4 @@ class SerialInfoImplJvmIrGenerator(
).apply { ).apply {
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name)) 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()
}
}
} }
@@ -27,10 +27,9 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.*
class SerializerForEnumsGenerator( class SerializerForEnumsGenerator(
irClass: IrClass, irClass: IrClass,
compilerContext: SerializationPluginContext, compilerContext: SerializationPluginContext,
bindingContext: BindingContext bindingContext: BindingContext,
) : serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator,
SerializerIrGenerator(irClass, compilerContext, bindingContext, null) { ) : SerializerIrGenerator(irClass, compilerContext, bindingContext, null, serialInfoJvmGenerator) {
override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc -> override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc ->
fun irThis(): IrExpression = fun irThis(): IrExpression =
IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol) IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol)
@@ -14,18 +14,14 @@ import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl import org.jetbrains.kotlin.ir.expressions.impl.*
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.mapValueParametersIndexed import org.jetbrains.kotlin.ir.expressions.mapValueParametersIndexed
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.ir.util.withReferenceScope
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -50,10 +46,9 @@ open class SerializerIrGenerator(
val irClass: IrClass, val irClass: IrClass,
final override val compilerContext: SerializationPluginContext, final override val compilerContext: SerializationPluginContext,
bindingContext: BindingContext, bindingContext: BindingContext,
metadataPlugin: SerializationDescriptorSerializerPlugin? metadataPlugin: SerializationDescriptorSerializerPlugin?,
) : private val serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator,
SerializerCodegen(irClass.descriptor, bindingContext, metadataPlugin), IrBuilderExtension { ) : SerializerCodegen(irClass.descriptor, bindingContext, metadataPlugin), IrBuilderExtension {
protected val serializableIrClass = compilerContext.symbolTable.referenceClass(serializableDescriptor).owner protected val serializableIrClass = compilerContext.symbolTable.referenceClass(serializableDescriptor).owner
protected val irAnySerialDescProperty = anySerialDescProperty?.let { compilerContext.symbolTable.referenceProperty(it) } protected val irAnySerialDescProperty = anySerialDescProperty?.let { compilerContext.symbolTable.referenceProperty(it) }
@@ -160,9 +155,24 @@ open class SerializerIrGenerator(
receiver: IrVariable, receiver: IrVariable,
method: IrFunctionSymbol method: IrFunctionSymbol
) { ) {
annotations.forEach { annotationCall -> for (annotationCall in annotations) {
if ((annotationCall.symbol.descriptor as? ClassConstructorDescriptor)?.constructedClass?.isSerialInfoAnnotation == true) val annotationClass = annotationCall.symbol.owner.parentAsClass
+irInvoke(irGet(receiver), method, annotationCall.deepCopyWithVariables()) 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, irClass: IrClass,
context: SerializationPluginContext, context: SerializationPluginContext,
bindingContext: BindingContext, bindingContext: BindingContext,
metadataPlugin: SerializationDescriptorSerializerPlugin? metadataPlugin: SerializationDescriptorSerializerPlugin?,
serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator,
) { ) {
val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return
if (serializableDesc.isSerializableEnum()) { if (serializableDesc.isSerializableEnum()) {
SerializerForEnumsGenerator(irClass, context, bindingContext).generate() SerializerForEnumsGenerator(irClass, context, bindingContext, serialInfoJvmGenerator).generate()
} else { } else {
SerializerIrGenerator(irClass, context, bindingContext, metadataPlugin).generate() SerializerIrGenerator(irClass, context, bindingContext, metadataPlugin, serialInfoJvmGenerator).generate()
} }
irClass.patchDeclarationParents(irClass.parent) irClass.patchDeclarationParents(irClass.parent)
} }
@@ -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.SerializableCompanionIrGenerator
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableIrGenerator import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableIrGenerator
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator 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. * Copy of [runOnFilePostfix], but this implementation first lowers declaration, then its children.
@@ -44,15 +45,16 @@ typealias SerializationPluginContext = IrPluginContext
private class SerializerClassLowering( private class SerializerClassLowering(
val context: SerializationPluginContext, val context: SerializationPluginContext,
val metadataPlugin: SerializationDescriptorSerializerPlugin? val metadataPlugin: SerializationDescriptorSerializerPlugin?
) : ) : IrElementTransformerVoid(), ClassLoweringPass {
IrElementTransformerVoid(), ClassLoweringPass { private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context)
override fun lower(irClass: IrClass) { override fun lower(irClass: IrClass) {
SerializableIrGenerator.generate(irClass, context, context.bindingContext) 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) SerializableCompanionIrGenerator.generate(irClass, context, context.bindingContext)
if (context.platform.isJvm()) { if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) {
SerialInfoImplJvmIrGenerator.generate(irClass, context) serialInfoJvmGenerator.generate(irClass)
} }
} }
} }