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.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrPropertyImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
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.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.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
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.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.backend.jvm.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
val BackendContext.externalSymbols: ReferenceSymbolTable get() = ir.symbols.externalSymbolTable
@@ -96,10 +83,11 @@ interface IrBuilderExtension {
) else compilerContext.externalSymbols.referenceConstructor(descriptor).owner
c.parent = this
c.returnType = descriptor.returnType.toIrType()
if (!fromStubs || overwriteValueParameters) c.createParameterDeclarations(receiver = null, overwriteValueParameters = overwriteValueParameters)
if (c.typeParameters.isEmpty()) {
c.copyTypeParamsFromDescriptor()
}
if (!fromStubs || overwriteValueParameters) c.createParameterDeclarations(
receiver = null,
overwriteValueParameters = overwriteValueParameters,
copyTypeParameters = false
)
c.body = compilerContext.createIrBuilder(c.symbol).at(this).irBlockBody(this.startOffset, this.endOffset) { bodyGen(c) }
this.addMember(c)
}
@@ -247,8 +235,10 @@ interface IrBuilderExtension {
propertyDescriptor
)
irProperty.parent = propertyParent
irProperty.backingField =
generatePropertyBackingField(propertyDescriptor, irProperty).apply { parent = propertyParent }
irProperty.backingField = generatePropertyBackingField(propertyDescriptor, irProperty).apply {
parent = propertyParent
correspondingProperty = irProperty
}
val fieldSymbol = irProperty.backingField!!.symbol
irProperty.getter = propertyDescriptor.getter?.let { generatePropertyAccessor(it, fieldSymbol) }
?.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(
this@createParameterDeclarations.startOffset, this@createParameterDeclarations.endOffset,
SERIALIZABLE_PLUGIN_ORIGIN,
@@ -377,7 +371,7 @@ interface IrBuilderExtension {
valueParameters.addAll(descriptor.valueParameters.map { it.irValueParameter() })
assert(typeParameters.isEmpty())
copyTypeParamsFromDescriptor()
if (copyTypeParameters) 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.irIfThen
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
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.SerialEntityNames.DECODER_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.STRUCTURE_DECODER_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,
primaryCtor.symbol
).apply {
ctor.typeParameters.forEachIndexed { index, irTypeParameter ->
irClass.typeParameters.forEachIndexed { index, irTypeParameter ->
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(
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) =
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? =
buildInitializersRemapping(serializableIrClass).run { { invoke(it.irField) } }
@@ -306,7 +302,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
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 =
IrGetValueImpl(startOffset, endOffset, loadFunc.dispatchReceiverParameter!!.symbol)
@@ -436,7 +432,12 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
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 {
@@ -449,4 +450,4 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
SerializerIrGenerator(irClass, context, bindingContext).generate()
}
}
}
}
@@ -29,10 +29,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlinx.serialization.compiler.backend.common.AbstractSerialGenerator
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.backend.common.*
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.ENCODER_CLASS
@@ -220,7 +217,7 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(codeg
// use iv == null to check only (do not emit serializer onto stack)
internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?,
iv: InstructionAdapter?, genericIndex: Int? = null, genericSerializerFieldGetter: (InstructionAdapter.(Int) -> Unit)? = null): Boolean {
if (genericIndex != null) {
if (maybeSerializer == null && genericIndex != null) {
// get field from serializer object
iv?.run { genericSerializerFieldGetter?.invoke(this, genericIndex) }
return true
@@ -406,4 +403,4 @@ fun InstructionAdapter.stackValueDefault(type: Type) {
DOUBLE -> dconst(0.0)
else -> aconst(null)
}
}
}
@@ -28,10 +28,9 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId
import org.jetbrains.kotlin.types.typeUtil.*
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName
internal fun isAllowedToHaveAutoGeneratedSerializerMethods(
@@ -97,7 +96,13 @@ internal val Annotations.serialTransient: Boolean
val KotlinType?.toClassDescriptor: ClassDescriptor?
@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
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 (3)
INVOKEINTERFACE (kotlinx/serialization/CompositeEncoder, endStructure, (Lkotlinx/serialization/SerialDescriptor;)V)
LABEL (L2)
LINENUMBER (13)
RETURN
LABEL (L3)
LABEL (L2)
}
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 (3)
INVOKEINTERFACE (kotlinx/serialization/CompositeEncoder, endStructure, (Lkotlinx/serialization/SerialDescriptor;)V)
LABEL (L7)
LINENUMBER (10)
RETURN
LABEL (L8)
LABEL (L7)
}
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 (3)
INVOKEINTERFACE (kotlinx/serialization/CompositeEncoder, endStructure, (Lkotlinx/serialization/SerialDescriptor;)V)
LABEL (L2)
LINENUMBER (7)
RETURN
LABEL (L3)
LABEL (L2)
}
public void serialize(kotlinx.serialization.Encoder encoder, java.lang.Object obj) {