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)
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,
@@ -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<KClass> 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<KClass<...>>.
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<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>()
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()
}
}
}
@@ -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)
@@ -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)
}
@@ -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)
}
}
}