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, protected val serializerDescriptor: ClassDescriptor,
bindingContext: BindingContext bindingContext: BindingContext
) : AbstractSerialGenerator(bindingContext, serializerDescriptor) { ) : AbstractSerialGenerator(bindingContext, serializerDescriptor) {
// protected val serializerDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext)
val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!! val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!!
protected val serialName: String = serializableDescriptor.annotations.serialNameValue ?: serializableDescriptor.fqNameUnsafe.asString() protected val serialName: String = serializableDescriptor.annotations.serialNameValue ?: serializableDescriptor.fqNameUnsafe.asString()
protected val properties = SerializableProperties(serializableDescriptor, bindingContext) protected val properties = SerializableProperties(serializableDescriptor, bindingContext)
@@ -61,9 +60,7 @@ abstract class SerializerCodegen(
return true return true
} }
protected open fun generateChildSerializersGetter(function: FunctionDescriptor) { protected abstract fun generateChildSerializersGetter(function: FunctionDescriptor)
// TODO()
}
protected val generatedSerialDescPropertyDescriptor = getPropertyToGenerate( protected val generatedSerialDescPropertyDescriptor = getPropertyToGenerate(
serializerDescriptor, SerialEntityNames.SERIAL_DESC_FIELD, serializerDescriptor, SerialEntityNames.SERIAL_DESC_FIELD,
@@ -73,9 +70,20 @@ abstract class SerializerCodegen(
serializerDescriptor::checkSerializableClassPropertyResult serializerDescriptor::checkSerializableClassPropertyResult
) { true } ) { 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 generateSerialDesc()
protected abstract fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) protected abstract fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor)
protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor) protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor)
@@ -129,7 +137,6 @@ abstract class SerializerCodegen(
) )
.singleOrNull { property -> .singleOrNull { property ->
isKindOk(property.kind) && isKindOk(property.kind) &&
property.modality != Modality.FINAL &&
property.returnType != null && property.returnType != null &&
isReturnTypeOk(property) isReturnTypeOk(property)
} }
@@ -58,11 +58,19 @@ interface IrBuilderExtension {
) else compilerContext.externalSymbols.referenceSimpleFunction(descriptor).owner ) else compilerContext.externalSymbols.referenceSimpleFunction(descriptor).owner
f.parent = this f.parent = this
f.returnType = descriptor.returnType!!.toIrType() f.returnType = descriptor.returnType!!.toIrType()
f.createParameterDeclarations() if (!fromStubs) f.createParameterDeclarations(this.thisReceiver)
f.body = compilerContext.createIrBuilder(f.symbol).irBlockBody { bodyGen(f) } f.body = compilerContext.createIrBuilder(f.symbol).irBlockBody { bodyGen(f) }
this.addMember(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( fun IrClass.contributeConstructor(
descriptor: ClassConstructorDescriptor, descriptor: ClassConstructorDescriptor,
bodyGen: IrBlockBodyBuilder.(IrConstructor) -> Unit bodyGen: IrBlockBodyBuilder.(IrConstructor) -> Unit
@@ -369,7 +377,7 @@ interface IrBuilderExtension {
} }
fun IrBuilderWithScope.serializerInstance( fun IrBuilderWithScope.serializerInstance(
enclosingGenerator: AbstractSerialGenerator, enclosingGenerator: SerializerIrGenerator,
serializableDescriptor: ClassDescriptor, serializableDescriptor: ClassDescriptor,
serializerClass: ClassDescriptor?, serializerClass: ClassDescriptor?,
module: ModuleDescriptor, module: ModuleDescriptor,
@@ -380,7 +388,9 @@ interface IrBuilderExtension {
compilerContext.externalSymbols.referenceClass(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer)) compilerContext.externalSymbols.referenceClass(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer))
if (serializerClass == null) { if (serializerClass == null) {
if (genericIndex == null) return 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) { if (serializerClass.kind == ClassKind.OBJECT) {
return irGetObject(serializerClass) 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.backend.common.BackendContext
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor 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.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.expressions.IrExpression 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.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
import org.jetbrains.kotlinx.serialization.compiler.resolve.classSerializer import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.findSerializerConstructorForTypeArgumentsSerializers
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor
class SerializableCompanionIrGenerator( class SerializableCompanionIrGenerator(
val irClass: IrClass, val irClass: IrClass,
@@ -40,13 +39,16 @@ class SerializableCompanionIrGenerator(
} }
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) = override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) =
irClass.contributeFunction(methodDescriptor, fromStubs = true) { _ -> irClass.contributeFunction(methodDescriptor, fromStubs = true) { getter ->
val serializer = serializableDescriptor.classSerializer?.toClassDescriptor!! val serializer = serializableDescriptor.classSerializer?.toClassDescriptor!!
val expr = if (serializer.kind == ClassKind.OBJECT) { val expr = if (serializer.kind == ClassKind.OBJECT) {
irGetObject(serializer) irGetObject(serializer)
} else { } else {
val ctor = compilerContext.externalSymbols.referenceConstructor(serializer.unsubstitutedPrimaryConstructor!!) val desc = requireNotNull(
val args: List<IrExpression> = emptyList() // todo 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()) irInvoke(null, ctor, *args.toTypedArray())
} }
+irReturn(expr) +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.BackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
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.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor 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.IrBlockBody import org.jetbrains.kotlin.ir.expressions.IrBlockBody
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.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.expressions.mapValueParameters import org.jetbrains.kotlin.ir.expressions.mapValueParameters
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
@@ -69,12 +66,17 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
introduceValueParameter(thisAsReceiverParameter) introduceValueParameter(thisAsReceiverParameter)
prop = generateSimplePropertyWithBackingField(thisAsReceiverParameter.symbol, desc, irClass) prop = generateSimplePropertyWithBackingField(thisAsReceiverParameter.symbol, desc, irClass)
irClass.addMember(prop) irClass.addMember(prop)
localSerializersFieldsDescriptors.forEach {
irClass.addMember(generateSimplePropertyWithBackingField(thisAsReceiverParameter.symbol, it, irClass))
}
} }
compilerContext.localSymbolTable.declareAnonymousInitializer( compilerContext.localSymbolTable.declareAnonymousInitializer(
irClass.startOffset, irClass.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, irClass.descriptor irClass.startOffset, irClass.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, irClass.descriptor
).buildWithScope { initIrBody -> ).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) val serialClassDescImplCtor = compilerContext.externalSymbols.referenceConstructor(serialDescImplConstructor)
compilerContext.localSymbolTable.withScope(initIrBody.descriptor) { compilerContext.localSymbolTable.withScope(initIrBody.descriptor) {
initIrBody.body = compilerContext.createIrBuilder(initIrBody.symbol).irBlockBody { initIrBody.body = compilerContext.createIrBuilder(initIrBody.symbol).irBlockBody {
@@ -106,16 +108,34 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
irGet(localDesc) irGet(localDesc)
) )
} }
// workaround for KT-25353 (ctor.body as? IrBlockBody)?.statements?.addAll(initIrBody.body.statements)
// irClass.addMember(initIrBody)
(ctor?.body as? IrBlockBody)?.statements?.addAll(initIrBody.body.statements)
} }
} }
} }
override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) { override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) =
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 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 -> override fun generateChildSerializersGetter(function: FunctionDescriptor) = irClass.contributeFunction(function) { irFun ->
val allSerializers = orderedProperties.map { requireNotNull( 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) translator.addProperty(propDesc, getterExpr, null)
} }
override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) { override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) {
val f = context.buildFunction(typedConstructorDescriptor) { jsFun, context -> val f = context.buildFunction(typedConstructorDescriptor) { jsFun, context ->
val thiz = jsFun.scope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME).makeRef() 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, _ -> serializableDescriptor.declaredTypeParameters.forEachIndexed { i, _ ->
codegen.v.newField( codegen.v.newField(
OtherOrigin(codegen.myClass.psiOrParent), ACC_PRIVATE or ACC_SYNTHETIC, 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.Variance
import org.jetbrains.kotlin.types.typeUtil.createProjection import org.jetbrains.kotlin.types.typeUtil.createProjection
import org.jetbrains.kotlin.types.typeUtil.makeNullable 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.IMPL_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_CLASS_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_CLASS_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
@@ -133,9 +134,10 @@ object KSerializerDescriptorResolver {
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
if (name == SerialEntityNames.SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) && if (name == SerialEntityNames.SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) &&
fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL } fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL }
) ) {
result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor)) result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor))
}
result.addAll(createLocalSerializersFieldsDescriptors(classDescriptor, thisDescriptor))
} }
fun generateCompanionObjectMethods( fun generateCompanionObjectMethods(
@@ -176,7 +178,7 @@ object KSerializerDescriptorResolver {
} }
} }
fun createSerializableClassPropertyDescriptor( private fun createSerializableClassPropertyDescriptor(
companionDescriptor: ClassDescriptor, companionDescriptor: ClassDescriptor,
classDescriptor: ClassDescriptor classDescriptor: ClassDescriptor
): PropertyDescriptor = ): PropertyDescriptor =
@@ -338,7 +340,7 @@ object KSerializerDescriptorResolver {
return constrDesc return constrDesc
} }
fun createSerializerGetterDescriptor(thisClass: ClassDescriptor, serializableClass: ClassDescriptor): SimpleFunctionDescriptor { private fun createSerializerGetterDescriptor(thisClass: ClassDescriptor, serializableClass: ClassDescriptor): SimpleFunctionDescriptor {
val f = SimpleFunctionDescriptorImpl.create( val f = SimpleFunctionDescriptorImpl.create(
thisClass, thisClass,
Annotations.EMPTY, 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)
}
}
} }