Fixes for IR serialization plugin

- Do not insert typeParameters into IrConstructor;
- Set correct property symbol to IrField;

- Use upper bound as a class descriptor for type parameters.
This will allow to get upper bound's KClass in polymorphic serialization.
Fixes https://github.com/Kotlin/kotlinx.serialization/issues/490;

- Lazily add all serializer functions.
Fixes https://github.com/Kotlin/kotlinx.serialization/issues/502;

- Don't generate constructor invocation for abstract(sealed) classes deserializer.
Such code is incorrect anyway (you can't create an instance of abstract class) and causes problems in devirtualization when exporting Native libraries.
This commit is contained in:
Leonid Startsev
2019-07-18 17:02:33 +03:00
parent 6494227c64
commit 3896992b12
5 changed files with 48 additions and 57 deletions
@@ -12,36 +12,23 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrPropertyImpl import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.types.makeNotNull
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
val BackendContext.externalSymbols: ReferenceSymbolTable get() = ir.symbols.externalSymbolTable val BackendContext.externalSymbols: ReferenceSymbolTable get() = ir.symbols.externalSymbolTable
@@ -96,10 +83,11 @@ interface IrBuilderExtension {
) else compilerContext.externalSymbols.referenceConstructor(descriptor).owner ) else compilerContext.externalSymbols.referenceConstructor(descriptor).owner
c.parent = this c.parent = this
c.returnType = descriptor.returnType.toIrType() c.returnType = descriptor.returnType.toIrType()
if (!fromStubs || overwriteValueParameters) c.createParameterDeclarations(receiver = null, overwriteValueParameters = overwriteValueParameters) if (!fromStubs || overwriteValueParameters) c.createParameterDeclarations(
if (c.typeParameters.isEmpty()) { receiver = null,
c.copyTypeParamsFromDescriptor() overwriteValueParameters = overwriteValueParameters,
} copyTypeParameters = false
)
c.body = compilerContext.createIrBuilder(c.symbol).at(this).irBlockBody(this.startOffset, this.endOffset) { bodyGen(c) } c.body = compilerContext.createIrBuilder(c.symbol).at(this).irBlockBody(this.startOffset, this.endOffset) { bodyGen(c) }
this.addMember(c) this.addMember(c)
} }
@@ -247,8 +235,10 @@ interface IrBuilderExtension {
propertyDescriptor propertyDescriptor
) )
irProperty.parent = propertyParent irProperty.parent = propertyParent
irProperty.backingField = irProperty.backingField = generatePropertyBackingField(propertyDescriptor, irProperty).apply {
generatePropertyBackingField(propertyDescriptor, irProperty).apply { parent = propertyParent } parent = propertyParent
correspondingProperty = irProperty
}
val fieldSymbol = irProperty.backingField!!.symbol val fieldSymbol = irProperty.backingField!!.symbol
irProperty.getter = propertyDescriptor.getter?.let { generatePropertyAccessor(it, fieldSymbol) } irProperty.getter = propertyDescriptor.getter?.let { generatePropertyAccessor(it, fieldSymbol) }
?.apply { parent = propertyParent } ?.apply { parent = propertyParent }
@@ -356,7 +346,11 @@ interface IrBuilderExtension {
} }
} }
fun IrFunction.createParameterDeclarations(receiver: IrValueParameter?, overwriteValueParameters: Boolean = false) { fun IrFunction.createParameterDeclarations(
receiver: IrValueParameter?,
overwriteValueParameters: Boolean = false,
copyTypeParameters: Boolean = true
) {
fun ParameterDescriptor.irValueParameter() = IrValueParameterImpl( fun ParameterDescriptor.irValueParameter() = IrValueParameterImpl(
this@createParameterDeclarations.startOffset, this@createParameterDeclarations.endOffset, this@createParameterDeclarations.startOffset, this@createParameterDeclarations.endOffset,
SERIALIZABLE_PLUGIN_ORIGIN, SERIALIZABLE_PLUGIN_ORIGIN,
@@ -377,7 +371,7 @@ interface IrBuilderExtension {
valueParameters.addAll(descriptor.valueParameters.map { it.irValueParameter() }) valueParameters.addAll(descriptor.valueParameters.map { it.irValueParameter() })
assert(typeParameters.isEmpty()) assert(typeParameters.isEmpty())
copyTypeParamsFromDescriptor() if (copyTypeParameters) copyTypeParamsFromDescriptor()
} }
fun IrFunction.copyTypeParamsFromDescriptor() { fun IrFunction.copyTypeParamsFromDescriptor() {
@@ -9,10 +9,7 @@ import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irIfThen import org.jetbrains.kotlin.backend.common.lower.irIfThen
import org.jetbrains.kotlin.backend.common.lower.irThrow import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
@@ -30,7 +27,6 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerialType
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_DECODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_DECODER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS
@@ -153,7 +149,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
compilerContext.irBuiltIns.unitType, compilerContext.irBuiltIns.unitType,
primaryCtor.symbol primaryCtor.symbol
).apply { ).apply {
ctor.typeParameters.forEachIndexed { index, irTypeParameter -> irClass.typeParameters.forEachIndexed { index, irTypeParameter ->
putTypeArgument(index, irTypeParameter.defaultType) putTypeArgument(index, irTypeParameter.defaultType)
} }
} }
@@ -171,7 +167,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
} }
override fun generateChildSerializersGetter(function: FunctionDescriptor) = irClass.contributeFunction(function) { irFun -> override fun generateChildSerializersGetter(function: FunctionDescriptor) = irClass.contributeFunction(function, fromStubs = true) { irFun ->
val allSerializers = serializableProperties.map { requireNotNull( val allSerializers = serializableProperties.map { requireNotNull(
serializerTower(this@SerializerIrGenerator, irFun.dispatchReceiverParameter!!, it)) { "Property ${it.name} must have a serializer" } serializerTower(this@SerializerIrGenerator, irFun.dispatchReceiverParameter!!, it)) { "Property ${it.name} must have a serializer" }
} }
@@ -188,7 +184,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
fun ClassDescriptor.referenceMethod(methodName: String) = fun ClassDescriptor.referenceMethod(methodName: String) =
getFuncDesc(methodName).single().let { compilerContext.externalSymbols.referenceFunction(it) } getFuncDesc(methodName).single().let { compilerContext.externalSymbols.referenceFunction(it) }
override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc -> override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function, fromStubs = true) { saveFunc ->
val fieldInitializer: (SerializableProperty) -> IrExpression? = val fieldInitializer: (SerializableProperty) -> IrExpression? =
buildInitializersRemapping(serializableIrClass).run { { invoke(it.irField) } } buildInitializersRemapping(serializableIrClass).run { { invoke(it.irField) } }
@@ -306,7 +302,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
defaultPrimitive to kType defaultPrimitive to kType
} }
override fun generateLoad(function: FunctionDescriptor) = irClass.contributeFunction(function) { loadFunc -> override fun generateLoad(function: FunctionDescriptor) = irClass.contributeFunction(function, fromStubs = true) { loadFunc ->
fun irThis(): IrExpression = fun irThis(): IrExpression =
IrGetValueImpl(startOffset, endOffset, loadFunc.dispatchReceiverParameter!!.symbol) IrGetValueImpl(startOffset, endOffset, loadFunc.dispatchReceiverParameter!!.symbol)
@@ -436,7 +432,12 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
compilerContext.externalSymbols.referenceConstructor(serializableDescriptor.unsubstitutedPrimaryConstructor!!) compilerContext.externalSymbols.referenceConstructor(serializableDescriptor.unsubstitutedPrimaryConstructor!!)
} }
+irReturn(irInvoke(null, ctor, typeArgs, args)) // todo: throw UnsupportedOperationException when feature branch with `open SerializerGenerator` (enums) will be merged
if (serializableDescriptor.modality != Modality.ABSTRACT && serializableDescriptor.modality != Modality.SEALED) {
+irReturn(irInvoke(null, ctor, typeArgs, args))
} else {
+irThrowNpe()
}
} }
companion object { companion object {
@@ -29,10 +29,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlinx.serialization.compiler.backend.common.AbstractSerialGenerator import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerialTypeInfo
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findAddOnSerializer
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS
@@ -220,7 +217,7 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(codeg
// use iv == null to check only (do not emit serializer onto stack) // use iv == null to check only (do not emit serializer onto stack)
internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?, internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?,
iv: InstructionAdapter?, genericIndex: Int? = null, genericSerializerFieldGetter: (InstructionAdapter.(Int) -> Unit)? = null): Boolean { iv: InstructionAdapter?, genericIndex: Int? = null, genericSerializerFieldGetter: (InstructionAdapter.(Int) -> Unit)? = null): Boolean {
if (genericIndex != null) { if (maybeSerializer == null && genericIndex != null) {
// get field from serializer object // get field from serializer object
iv?.run { genericSerializerFieldGetter?.invoke(this, genericIndex) } iv?.run { genericSerializerFieldGetter?.invoke(this, genericIndex) }
return true return true
@@ -28,10 +28,9 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.*
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName
internal fun isAllowedToHaveAutoGeneratedSerializerMethods( internal fun isAllowedToHaveAutoGeneratedSerializerMethods(
@@ -97,7 +96,13 @@ internal val Annotations.serialTransient: Boolean
val KotlinType?.toClassDescriptor: ClassDescriptor? val KotlinType?.toClassDescriptor: ClassDescriptor?
@JvmName("toClassDescriptor") @JvmName("toClassDescriptor")
get() = this?.constructor?.declarationDescriptor as? ClassDescriptor get() = this?.constructor?.declarationDescriptor?.let { descriptor ->
when(descriptor) {
is ClassDescriptor -> descriptor
is TypeParameterDescriptor -> descriptor.representativeUpperBound.toClassDescriptor
else -> null
}
}
internal val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean internal val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean
get() = this.kind == ClassKind.CLASS && annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName) get() = this.kind == ClassKind.CLASS && annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)
@@ -188,10 +188,8 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa
ALOAD (4) ALOAD (4)
ALOAD (3) ALOAD (3)
INVOKEINTERFACE (kotlinx/serialization/CompositeEncoder, endStructure, (Lkotlinx/serialization/SerialDescriptor;)V) INVOKEINTERFACE (kotlinx/serialization/CompositeEncoder, endStructure, (Lkotlinx/serialization/SerialDescriptor;)V)
LABEL (L2)
LINENUMBER (13)
RETURN RETURN
LABEL (L3) LABEL (L2)
} }
public void serialize(kotlinx.serialization.Encoder encoder, java.lang.Object obj) { public void serialize(kotlinx.serialization.Encoder encoder, java.lang.Object obj) {
@@ -493,10 +491,8 @@ public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serializ
ALOAD (4) ALOAD (4)
ALOAD (3) ALOAD (3)
INVOKEINTERFACE (kotlinx/serialization/CompositeEncoder, endStructure, (Lkotlinx/serialization/SerialDescriptor;)V) INVOKEINTERFACE (kotlinx/serialization/CompositeEncoder, endStructure, (Lkotlinx/serialization/SerialDescriptor;)V)
LABEL (L7)
LINENUMBER (10)
RETURN RETURN
LABEL (L8) LABEL (L7)
} }
public void serialize(kotlinx.serialization.Encoder encoder, java.lang.Object obj) { public void serialize(kotlinx.serialization.Encoder encoder, java.lang.Object obj) {
@@ -840,10 +836,8 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in
ALOAD (4) ALOAD (4)
ALOAD (3) ALOAD (3)
INVOKEINTERFACE (kotlinx/serialization/CompositeEncoder, endStructure, (Lkotlinx/serialization/SerialDescriptor;)V) INVOKEINTERFACE (kotlinx/serialization/CompositeEncoder, endStructure, (Lkotlinx/serialization/SerialDescriptor;)V)
LABEL (L2)
LINENUMBER (7)
RETURN RETURN
LABEL (L3) LABEL (L2)
} }
public void serialize(kotlinx.serialization.Encoder encoder, java.lang.Object obj) { public void serialize(kotlinx.serialization.Encoder encoder, java.lang.Object obj) {