Generics serialization in IR

This commit is contained in:
Leonid Startsev
2018-10-03 16:53:27 +03:00
parent a4a1df0a81
commit 077f51e2f4
8 changed files with 129 additions and 34 deletions
@@ -30,7 +30,6 @@ abstract class SerializerCodegen(
protected val serializerDescriptor: ClassDescriptor,
bindingContext: BindingContext
) : AbstractSerialGenerator(bindingContext, serializerDescriptor) {
// protected val serializerDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext)
val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!!
protected val serialName: String = serializableDescriptor.annotations.serialNameValue ?: serializableDescriptor.fqNameUnsafe.asString()
protected val properties = SerializableProperties(serializableDescriptor, bindingContext)
@@ -61,9 +60,7 @@ abstract class SerializerCodegen(
return true
}
protected open fun generateChildSerializersGetter(function: FunctionDescriptor) {
// TODO()
}
protected abstract fun generateChildSerializersGetter(function: FunctionDescriptor)
protected val generatedSerialDescPropertyDescriptor = getPropertyToGenerate(
serializerDescriptor, SerialEntityNames.SERIAL_DESC_FIELD,
@@ -73,9 +70,20 @@ abstract class SerializerCodegen(
serializerDescriptor::checkSerializableClassPropertyResult
) { true }
val localSerializersFieldsDescriptors: List<PropertyDescriptor> = findLocalSerializersFieldDescriptors()
private fun findLocalSerializersFieldDescriptors(): List<PropertyDescriptor> {
val count = serializableDescriptor.declaredTypeParameters.size
if (count == 0) return emptyList()
val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" }
return propNames.mapNotNull { name ->
getPropertyToGenerate(serializerDescriptor, name) { isKSerializer(it.returnType) }
}
}
protected abstract fun generateSerialDesc()
protected abstract fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor)
protected abstract fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor)
protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor)
@@ -129,7 +137,6 @@ abstract class SerializerCodegen(
)
.singleOrNull { property ->
isKindOk(property.kind) &&
property.modality != Modality.FINAL &&
property.returnType != null &&
isReturnTypeOk(property)
}
@@ -58,11 +58,19 @@ interface IrBuilderExtension {
) else compilerContext.externalSymbols.referenceSimpleFunction(descriptor).owner
f.parent = this
f.returnType = descriptor.returnType!!.toIrType()
f.createParameterDeclarations()
if (!fromStubs) f.createParameterDeclarations(this.thisReceiver)
f.body = compilerContext.createIrBuilder(f.symbol).irBlockBody { bodyGen(f) }
this.addMember(f)
}
fun IrClass.contributeCtor(descriptor: ClassConstructorDescriptor, bodyGen: IrBlockBodyBuilder.(IrFunction) -> Unit) {
val c = compilerContext.externalSymbols.referenceConstructor(descriptor).owner
c.parent = this
c.returnType = descriptor.returnType.toIrType()
c.body = compilerContext.createIrBuilder(c.symbol).irBlockBody { bodyGen(c) }
this.addMember(c)
}
fun IrClass.contributeConstructor(
descriptor: ClassConstructorDescriptor,
bodyGen: IrBlockBodyBuilder.(IrConstructor) -> Unit
@@ -369,7 +377,7 @@ interface IrBuilderExtension {
}
fun IrBuilderWithScope.serializerInstance(
enclosingGenerator: AbstractSerialGenerator,
enclosingGenerator: SerializerIrGenerator,
serializableDescriptor: ClassDescriptor,
serializerClass: ClassDescriptor?,
module: ModuleDescriptor,
@@ -380,7 +388,9 @@ interface IrBuilderExtension {
compilerContext.externalSymbols.referenceClass(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer))
if (serializerClass == null) {
if (genericIndex == null) return null
TODO("Saved serializer for generic argument")
val thiz = enclosingGenerator.irClass.thisReceiver!!
val prop = enclosingGenerator.localSerializersFieldsDescriptors[genericIndex]
return irGetField(irGet(thiz), compilerContext.localSymbolTable.referenceField(prop).owner)
}
if (serializerClass.kind == ClassKind.OBJECT) {
return irGetObject(serializerClass)
@@ -3,6 +3,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -11,10 +12,8 @@ import org.jetbrains.kotlin.ir.util.TypeTranslator
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
import org.jetbrains.kotlinx.serialization.compiler.resolve.classSerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.findSerializerConstructorForTypeArgumentsSerializers
class SerializableCompanionIrGenerator(
val irClass: IrClass,
@@ -40,13 +39,16 @@ class SerializableCompanionIrGenerator(
}
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) =
irClass.contributeFunction(methodDescriptor, fromStubs = true) { _ ->
irClass.contributeFunction(methodDescriptor, fromStubs = true) { getter ->
val serializer = serializableDescriptor.classSerializer?.toClassDescriptor!!
val expr = if (serializer.kind == ClassKind.OBJECT) {
irGetObject(serializer)
} else {
val ctor = compilerContext.externalSymbols.referenceConstructor(serializer.unsubstitutedPrimaryConstructor!!)
val args: List<IrExpression> = emptyList() // todo
val desc = requireNotNull(
KSerializerDescriptorResolver.findSerializerConstructorForTypeArgumentsSerializers(serializer)
) { "Generated serializer does not have constructor with required number of arguments" }
val ctor = compilerContext.externalSymbols.referenceConstructor(desc)
val args: List<IrExpression> = getter.valueParameters.map { irGet(it) }
irInvoke(null, ctor, *args.toTypedArray())
}
+irReturn(expr)
@@ -8,18 +8,15 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
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.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
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.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.expressions.mapValueParameters
import org.jetbrains.kotlin.ir.expressions.mapValueParametersIndexed
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
@@ -69,12 +66,17 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
introduceValueParameter(thisAsReceiverParameter)
prop = generateSimplePropertyWithBackingField(thisAsReceiverParameter.symbol, desc, irClass)
irClass.addMember(prop)
localSerializersFieldsDescriptors.forEach {
irClass.addMember(generateSimplePropertyWithBackingField(thisAsReceiverParameter.symbol, it, irClass))
}
}
compilerContext.localSymbolTable.declareAnonymousInitializer(
irClass.startOffset, irClass.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, irClass.descriptor
).buildWithScope { initIrBody ->
val ctor = irClass.declarations.filterIsInstance<IrConstructor>().singleOrNull()
val ctor = irClass.declarations.filterIsInstance<IrConstructor>().find { it.isPrimary }
?: throw AssertionError("Serializer must have primary constructor")
val serialClassDescImplCtor = compilerContext.externalSymbols.referenceConstructor(serialDescImplConstructor)
compilerContext.localSymbolTable.withScope(initIrBody.descriptor) {
initIrBody.body = compilerContext.createIrBuilder(initIrBody.symbol).irBlockBody {
@@ -106,16 +108,34 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
irGet(localDesc)
)
}
// workaround for KT-25353
// irClass.addMember(initIrBody)
(ctor?.body as? IrBlockBody)?.statements?.addAll(initIrBody.body.statements)
(ctor.body as? IrBlockBody)?.statements?.addAll(initIrBody.body.statements)
}
}
}
override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) =
irClass.contributeCtor(typedConstructorDescriptor) { ctor ->
// generate call to primary ctor to init serialClassDesc and super()
val primaryCtor = irClass.descriptor.unsubstitutedPrimaryConstructor
?: throw AssertionError("Serializer class must have primary constructor")
+IrDelegatingConstructorCallImpl(
startOffset,
endOffset,
compilerContext.irBuiltIns.unitType,
compilerContext.localSymbolTable.referenceConstructor(primaryCtor),
primaryCtor
)
// store type arguments serializers in fields
val thisAsReceiverParameter = irClass.thisReceiver!!
ctor.valueParameters.forEachIndexed { index, param ->
val localSerial = compilerContext.localSymbolTable.referenceField(localSerializersFieldsDescriptors[index])
+irSetField(generateReceiverExpressionForFieldAccess(
thisAsReceiverParameter.symbol,
localSerializersFieldsDescriptors[index]
), localSerial.owner, irGet(param))
}
}
override fun generateChildSerializersGetter(function: FunctionDescriptor) = irClass.contributeFunction(function) { irFun ->
val allSerializers = orderedProperties.map { requireNotNull(
@@ -0,0 +1,39 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.FieldDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
/**
* Simple property descriptor with backing field without getters/setters for ir generation purposes
*/
class SimpleSyntheticPropertyDescriptor(
owner: ClassDescriptor,
name: String,
type: KotlinType,
isVar: Boolean = false,
visibility: Visibility = Visibilities.PRIVATE
) : PropertyDescriptorImpl(
owner,
null,
Annotations.EMPTY,
Modality.FINAL,
visibility,
isVar,
Name.identifier(name),
CallableMemberDescriptor.Kind.SYNTHESIZED,
owner.source,
false, false, false, false, false, false
) {
private val _backingField = FieldDescriptorImpl(Annotations.EMPTY, this)
init {
super.setType(type, emptyList(), owner.thisAsReceiverParameter, null)
super.initialize(null, null, _backingField, null)
}
}
@@ -107,7 +107,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
translator.addProperty(propDesc, getterExpr, null)
}
override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) {
override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) {
val f = context.buildFunction(typedConstructorDescriptor) { jsFun, context ->
val thiz = jsFun.scope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME).makeRef()
@@ -50,7 +50,7 @@ class SerializerCodegenImpl(
}
}
override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) {
override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) {
serializableDescriptor.declaredTypeParameters.forEachIndexed { i, _ ->
codegen.v.newField(
OtherOrigin(codegen.myClass.psiOrParent), ACC_PRIVATE or ACC_SYNTHETIC,
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.createProjection
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SimpleSyntheticPropertyDescriptor
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.IMPL_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_CLASS_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
@@ -133,9 +134,10 @@ object KSerializerDescriptorResolver {
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
if (name == SerialEntityNames.SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) &&
fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL }
)
) {
result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor))
}
result.addAll(createLocalSerializersFieldsDescriptors(classDescriptor, thisDescriptor))
}
fun generateCompanionObjectMethods(
@@ -176,7 +178,7 @@ object KSerializerDescriptorResolver {
}
}
fun createSerializableClassPropertyDescriptor(
private fun createSerializableClassPropertyDescriptor(
companionDescriptor: ClassDescriptor,
classDescriptor: ClassDescriptor
): PropertyDescriptor =
@@ -338,7 +340,7 @@ object KSerializerDescriptorResolver {
return constrDesc
}
fun createSerializerGetterDescriptor(thisClass: ClassDescriptor, serializableClass: ClassDescriptor): SimpleFunctionDescriptor {
private fun createSerializerGetterDescriptor(thisClass: ClassDescriptor, serializableClass: ClassDescriptor): SimpleFunctionDescriptor {
val f = SimpleFunctionDescriptorImpl.create(
thisClass,
Annotations.EMPTY,
@@ -493,4 +495,19 @@ object KSerializerDescriptorResolver {
)
}
}
// create properties typeSerial0, typeSerial1, etc... for storing generic arguments' serializers
private fun createLocalSerializersFieldsDescriptors(serializableDescriptor: ClassDescriptor, serializerDescriptor: ClassDescriptor): List<PropertyDescriptor> {
if (serializableDescriptor.declaredTypeParameters.isEmpty()) return emptyList()
val serializerClass = serializableDescriptor.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
return serializerDescriptor.declaredTypeParameters.mapIndexed { index, param ->
val pType =
KotlinTypeFactory.simpleNotNullType(
Annotations.EMPTY,
serializerClass,
listOf(TypeProjectionImpl(param.defaultType))
)
SimpleSyntheticPropertyDescriptor(serializerDescriptor, "$typeArgPrefix$index", pType)
}
}
}