Support generic classes in serialization FIR plugin

This commit is contained in:
Leonid Startsev
2022-08-10 18:59:18 +02:00
committed by Space
parent 805fc747b9
commit acd6180c30
21 changed files with 594 additions and 356 deletions
@@ -305,7 +305,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
property.type,
genericIndex = property.genericIndex
) { it, _ ->
val (_, ir) = generator.localSerializersFieldsDescriptors[it]
val ir = generator.localSerializersFieldsDescriptors[it]
irGetField(irGet(dispatchReceiverParameter), ir.backingField!!)
}?.let { expr -> wrapWithNullableSerializerIfNeeded(property.type, expr, nullableSerClass) }
}
@@ -7,11 +7,12 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.deepCopyWithVariables
import org.jetbrains.kotlin.ir.expressions.*
@@ -33,7 +34,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_FQ
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_MODE_FQ
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_PUBLICATION_MODE_NAME
@@ -99,15 +99,7 @@ interface IrBuilderWithPluginContext {
val lazyIrClass = compilerContext.referenceClass(ClassId.topLevel(LAZY_FQ))!!.owner
val lazyIrType = lazyIrClass.defaultType.substitute(mapOf(lazyIrClass.typeParameters[0].symbol to targetIrType))
val propertyDescriptor =
KSerializerDescriptorResolver.createValPropertyDescriptor(
Name.identifier(name.asString() + "\$delegate"),
containingClass.descriptor,
lazyIrType.toKotlinType(),
createGetter = true
)
return generateSimplePropertyWithBackingField(propertyDescriptor, containingClass).apply {
return generateSimplePropertyWithBackingField(Name.identifier(name.asString() + "\$delegate"), lazyIrType, containingClass).apply {
val builder = DeclarationIrBuilder(compilerContext, containingClass.symbol, startOffset, endOffset)
val initializerBody = builder.run {
val enumElement = IrGetEnumValueImpl(
@@ -133,11 +125,7 @@ interface IrBuilderWithPluginContext {
name: Name,
initializerBuilder: IrBlockBodyBuilder.() -> Unit
): IrProperty {
val targetKotlinType = type.toKotlinType()
val propertyDescriptor =
KSerializerDescriptorResolver.createValPropertyDescriptor(name, companionClass.descriptor, targetKotlinType)
return generateSimplePropertyWithBackingField(propertyDescriptor, companionClass, name).apply {
return generateSimplePropertyWithBackingField(name, type, companionClass).apply {
companionClass.contributeAnonymousInitializer {
val irBlockBody = irBlockBody(startOffset, endOffset, initializerBuilder)
irBlockBody.statements.dropLast(1).forEach { +it }
@@ -292,209 +280,61 @@ interface IrBuilderWithPluginContext {
}
}
private inline fun <reified T : IrDeclaration> IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? {
return declarations.singleOrNull { it.descriptor == descriptor } as? T
}
fun generateSimplePropertyWithBackingField(
propertyDescriptor: PropertyDescriptor,
propertyName: Name,
propertyType: IrType,
propertyParent: IrClass,
fieldName: Name = propertyDescriptor.name,
visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE
): IrProperty = generatePropertyMissingParts(null, propertyName, propertyType, propertyParent, visibility)
fun generatePropertyMissingParts(
property: IrProperty?,
propertyName: Name,
propertyType: IrType,
propertyParent: IrClass,
visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE
): IrProperty {
val irProperty = propertyParent.searchForDeclaration(propertyDescriptor) ?: run {
with(propertyDescriptor) {
propertyParent.factory.createProperty(
propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrPropertySymbolImpl(propertyDescriptor),
name, visibility, modality, isVar, isConst, isLateInit, isDelegated, isExternal
).also {
it.parent = propertyParent
propertyParent.addMember(it)
}
}
val field = property?.backingField ?: propertyParent.factory.buildField {
startOffset = propertyParent.startOffset
endOffset = propertyParent.endOffset
name = propertyName
type = propertyType
origin = SERIALIZABLE_PLUGIN_ORIGIN
isFinal = true
this.visibility = DescriptorVisibilities.PRIVATE
}.also { it.parent = propertyParent }
val prop = property ?: propertyParent.addProperty {
startOffset = propertyParent.startOffset
endOffset = propertyParent.endOffset
name = propertyName
this.isVar = false
origin = SERIALIZABLE_PLUGIN_ORIGIN
}
propertyParent.generatePropertyBackingFieldIfNeeded(propertyDescriptor, irProperty, fieldName)
val fieldSymbol = irProperty.backingField!!.symbol
irProperty.getter = propertyDescriptor.getter?.let {
propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = true)
}?.apply { parent = propertyParent }
irProperty.setter = propertyDescriptor.setter?.let {
propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = false)
}?.apply { parent = propertyParent }
return irProperty
}
fun IrType.kClassToJClassIfNeeded(): IrType = this
fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression = irExpression
private fun IrClass.generatePropertyBackingFieldIfNeeded(
propertyDescriptor: PropertyDescriptor,
originProperty: IrProperty,
name: Name,
) {
if (originProperty.backingField != null) return
val field = with(propertyDescriptor) {
@OptIn(FirIncompatiblePluginAPI::class)// should be called only with old FE
originProperty.factory.createField(
originProperty.startOffset, originProperty.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrFieldSymbolImpl(propertyDescriptor), name, type.toIrType(),
visibility, !isVar, isEffectivelyExternal(), dispatchReceiverParameter == null
)
}
field.apply {
parent = this@generatePropertyBackingFieldIfNeeded
correspondingPropertySymbol = originProperty.symbol
prop.apply {
field.correspondingPropertySymbol = this.symbol
backingField = field
}
originProperty.backingField = field
}
private fun IrClass.generatePropertyAccessor(
propertyDescriptor: PropertyDescriptor,
property: IrProperty,
descriptor: PropertyAccessorDescriptor,
fieldSymbol: IrFieldSymbol,
isGetter: Boolean,
): IrSimpleFunction {
val irAccessor: IrSimpleFunction = when (isGetter) {
true -> searchForDeclaration<IrProperty>(propertyDescriptor)?.getter
false -> searchForDeclaration<IrProperty>(propertyDescriptor)?.setter
} ?: run {
with(descriptor) {
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
property.factory.createFunction(
fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor),
name, visibility, modality, returnType!!.toIrType(),
isInline, isEffectivelyExternal(), isTailrec, isSuspend, isOperator, isInfix, isExpect
)
}.also { f ->
generateOverriddenFunctionSymbols(f, compilerContext.symbolTable)
f.createParameterDeclarations(descriptor)
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
f.returnType = descriptor.returnType!!.toIrType()
f.correspondingPropertySymbol = fieldSymbol.owner.correspondingPropertySymbol
}
val getter = prop.getter ?: prop.addGetter {
startOffset = propertyParent.startOffset
endOffset = propertyParent.endOffset
returnType = propertyType
origin = SERIALIZABLE_PLUGIN_ORIGIN
this.visibility = visibility
modality = Modality.FINAL
}
irAccessor.body = when (isGetter) {
true -> generateDefaultGetterBody(irAccessor)
false -> generateDefaultSetterBody(irAccessor)
getter.apply {
if (dispatchReceiverParameter == null)
dispatchReceiverParameter = propertyParent.thisReceiver!!.copyTo(this, type = propertyParent.defaultType)
if (body == null)
body = compilerContext.irBuiltIns.createIrBuilder(symbol, propertyParent.startOffset, propertyParent.endOffset).irBlockBody {
+irReturn(irGetField(irGet(dispatchReceiverParameter!!), field))
}
}
return irAccessor
}
private fun generateDefaultGetterBody(
irAccessor: IrSimpleFunction
): IrBlockBody {
val irProperty = irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}")
val startOffset = irAccessor.startOffset
val endOffset = irAccessor.endOffset
val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset)
val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol)
val propertyIrType = irAccessor.returnType
irBody.statements.add(
IrReturnImpl(
startOffset, endOffset, compilerContext.irBuiltIns.nothingType,
irAccessor.symbol,
IrGetFieldImpl(
startOffset, endOffset,
irProperty.backingField?.symbol ?: error("Property expected to have backing field"),
propertyIrType,
receiver
).let {
if (propertyIrType.isKClass()) {
irAccessor.returnType = irAccessor.returnType.kClassToJClassIfNeeded()
kClassExprToJClassIfNeeded(startOffset, endOffset, it)
} else it
}
)
)
return irBody
}
private fun generateDefaultSetterBody(
irAccessor: IrSimpleFunction
): IrBlockBody {
val irProperty = irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}")
val startOffset = irAccessor.startOffset
val endOffset = irAccessor.endOffset
val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset)
val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol)
val irValueParameter = irAccessor.valueParameters.single()
irBody.statements.add(
IrSetFieldImpl(
startOffset, endOffset,
irProperty.backingField?.symbol ?: error("Property ${irProperty.render()} expected to have backing field"),
receiver,
IrGetValueImpl(startOffset, endOffset, irValueParameter.type, irValueParameter.symbol),
compilerContext.irBuiltIns.unitType
)
)
return irBody
}
fun generateReceiverExpressionForFieldAccess(
ownerSymbol: IrValueSymbol
): IrExpression = IrGetValueImpl(
ownerSymbol.owner.startOffset, ownerSymbol.owner.endOffset,
ownerSymbol
)
fun IrFunction.createParameterDeclarations(
descriptor: FunctionDescriptor,
overwriteValueParameters: Boolean = false,
copyTypeParameters: Boolean = true
) {
val function = this
fun irValueParameter(descriptor: ParameterDescriptor): IrValueParameter = with(descriptor) {
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
factory.createValueParameter(
function.startOffset, function.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this),
name, indexOrMinusOne, type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline,
isHidden = false, isAssignable = false
).also {
it.parent = function
}
}
if (copyTypeParameters) {
assert(typeParameters.isEmpty())
copyTypeParamsFromDescriptor(descriptor)
}
dispatchReceiverParameter = descriptor.dispatchReceiverParameter?.let { irValueParameter(it) }
extensionReceiverParameter = descriptor.extensionReceiverParameter?.let { irValueParameter(it) }
if (!overwriteValueParameters)
assert(valueParameters.isEmpty())
valueParameters = descriptor.valueParameters.map { irValueParameter(it) }
}
fun IrFunction.copyTypeParamsFromDescriptor(descriptor: FunctionDescriptor) {
val newTypeParameters = descriptor.typeParameters.map {
factory.createTypeParameter(
startOffset, endOffset,
SERIALIZABLE_PLUGIN_ORIGIN,
IrTypeParameterSymbolImpl(it),
it.name, it.index, it.isReified, it.variance
).also { typeParameter ->
typeParameter.parent = this
}
}
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
newTypeParameters.forEach { typeParameter ->
typeParameter.superTypes = typeParameter.descriptor.upperBounds.map { it.toIrType() }
}
typeParameters = newTypeParameters
return prop
}
fun createClassReference(classType: IrType, startOffset: Int, endOffset: Int): IrClassReference {
@@ -35,6 +35,8 @@ internal fun IrType.isKSerializer(): Boolean {
return fqName == SerialEntityNames.KSERIALIZER_NAME_FQ || fqName == SerialEntityNames.GENERATED_SERIALIZER_FQ
}
internal fun IrType.isGeneratedKSerializer(): Boolean = classifierOrNull?.isClassWithFqName(SerialEntityNames.GENERATED_SERIALIZER_FQ.toUnsafe()) == true
internal val IrClass.isInternalSerializable: Boolean
get() {
if (kind != ClassKind.CLASS) return false
@@ -7,32 +7,29 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI
import org.jetbrains.kotlin.backend.common.ir.addExtensionReceiver
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
@@ -115,13 +112,13 @@ class SerialInfoImplJvmIrGenerator(
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name))
}
override fun IrType.kClassToJClassIfNeeded(): IrType = when {
private fun IrType.kClassToJClassIfNeeded(): IrType = when {
this.isKClass() -> javaLangType
this.isKClassArray() -> compilerContext.irBuiltIns.arrayClass.typeWith(javaLangType)
else -> this
}
override fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression {
private fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression {
val getterSymbol = kClassJava.owner.getter!!.symbol
return IrCallImpl(
startOffset, endOffset,
@@ -149,7 +146,15 @@ class SerialInfoImplJvmIrGenerator(
addGetter().apply {
annotations = listOf(
IrConstructorCallImpl.fromSymbolOwner(jvmName.typeWith(), jvmName.constructors.single()).apply {
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, "getJavaClass"))
putValueArgument(
0,
IrConstImpl.string(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
context.irBuiltIns.stringType,
"getJavaClass"
)
)
}
)
addExtensionReceiver(context.irBuiltIns.kClassClass.starProjectedType)
@@ -180,4 +185,227 @@ class SerialInfoImplJvmIrGenerator(
createImplicitParameterDeclarationWithWrappedDescriptor()
block(this)
}.symbol
private inline fun <reified T : IrDeclaration> IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? {
return declarations.singleOrNull { it.descriptor == descriptor } as? T
}
private fun generateSimplePropertyWithBackingField(
propertyDescriptor: PropertyDescriptor,
propertyParent: IrClass,
fieldName: Name = propertyDescriptor.name,
): IrProperty {
val irProperty = propertyParent.searchForDeclaration(propertyDescriptor) ?: run {
with(propertyDescriptor) {
propertyParent.factory.createProperty(
propertyParent.startOffset,
propertyParent.endOffset,
SERIALIZABLE_PLUGIN_ORIGIN,
IrPropertySymbolImpl(propertyDescriptor),
name,
visibility,
modality,
isVar,
isConst,
isLateInit,
isDelegated,
isExternal
).also {
it.parent = propertyParent
propertyParent.addMember(it)
}
}
}
propertyParent.generatePropertyBackingFieldIfNeeded(propertyDescriptor, irProperty, fieldName)
val fieldSymbol = irProperty.backingField!!.symbol
irProperty.getter = propertyDescriptor.getter?.let {
propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = true)
}?.apply { parent = propertyParent }
irProperty.setter = propertyDescriptor.setter?.let {
propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = false)
}?.apply { parent = propertyParent }
return irProperty
}
private fun IrClass.generatePropertyBackingFieldIfNeeded(
propertyDescriptor: PropertyDescriptor,
originProperty: IrProperty,
name: Name,
) {
if (originProperty.backingField != null) return
val field = with(propertyDescriptor) {
@OptIn(FirIncompatiblePluginAPI::class)// should be called only with old FE
originProperty.factory.createField(
originProperty.startOffset,
originProperty.endOffset,
SERIALIZABLE_PLUGIN_ORIGIN,
IrFieldSymbolImpl(propertyDescriptor),
name,
type.toIrType(),
visibility,
!isVar,
isEffectivelyExternal(),
dispatchReceiverParameter == null
)
}
field.apply {
parent = this@generatePropertyBackingFieldIfNeeded
correspondingPropertySymbol = originProperty.symbol
}
originProperty.backingField = field
}
private fun IrClass.generatePropertyAccessor(
propertyDescriptor: PropertyDescriptor,
property: IrProperty,
descriptor: PropertyAccessorDescriptor,
fieldSymbol: IrFieldSymbol,
isGetter: Boolean,
): IrSimpleFunction {
val irAccessor: IrSimpleFunction = when (isGetter) {
true -> searchForDeclaration<IrProperty>(propertyDescriptor)?.getter
false -> searchForDeclaration<IrProperty>(propertyDescriptor)?.setter
} ?: run {
with(descriptor) {
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
property.factory.createFunction(
fieldSymbol.owner.startOffset,
fieldSymbol.owner.endOffset,
SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor),
name, visibility, modality, returnType!!.toIrType(),
isInline, isEffectivelyExternal(), isTailrec, isSuspend, isOperator, isInfix, isExpect
)
}.also { f ->
generateOverriddenFunctionSymbols(f, compilerContext.symbolTable)
f.createParameterDeclarations(descriptor)
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
f.returnType = descriptor.returnType!!.toIrType()
f.correspondingPropertySymbol = fieldSymbol.owner.correspondingPropertySymbol
}
}
irAccessor.body = when (isGetter) {
true -> generateDefaultGetterBody(irAccessor)
false -> generateDefaultSetterBody(irAccessor)
}
return irAccessor
}
private fun generateDefaultGetterBody(
irAccessor: IrSimpleFunction
): IrBlockBody {
val irProperty =
irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}")
val startOffset = irAccessor.startOffset
val endOffset = irAccessor.endOffset
val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset)
val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol)
val propertyIrType = irAccessor.returnType
irBody.statements.add(
IrReturnImpl(
startOffset, endOffset, compilerContext.irBuiltIns.nothingType,
irAccessor.symbol,
IrGetFieldImpl(
startOffset, endOffset,
irProperty.backingField?.symbol ?: error("Property expected to have backing field"),
propertyIrType,
receiver
).let {
if (propertyIrType.isKClass()) {
irAccessor.returnType = irAccessor.returnType.kClassToJClassIfNeeded()
kClassExprToJClassIfNeeded(startOffset, endOffset, it)
} else it
}
)
)
return irBody
}
private fun generateDefaultSetterBody(
irAccessor: IrSimpleFunction
): IrBlockBody {
val irProperty =
irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}")
val startOffset = irAccessor.startOffset
val endOffset = irAccessor.endOffset
val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset)
val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol)
val irValueParameter = irAccessor.valueParameters.single()
irBody.statements.add(
IrSetFieldImpl(
startOffset, endOffset,
irProperty.backingField?.symbol ?: error("Property ${irProperty.render()} expected to have backing field"),
receiver,
IrGetValueImpl(startOffset, endOffset, irValueParameter.type, irValueParameter.symbol),
compilerContext.irBuiltIns.unitType
)
)
return irBody
}
private fun generateReceiverExpressionForFieldAccess(
ownerSymbol: IrValueSymbol
): IrExpression = IrGetValueImpl(
ownerSymbol.owner.startOffset, ownerSymbol.owner.endOffset,
ownerSymbol
)
private fun IrFunction.createParameterDeclarations(
descriptor: FunctionDescriptor,
overwriteValueParameters: Boolean = false,
copyTypeParameters: Boolean = true
) {
val function = this
fun irValueParameter(descriptor: ParameterDescriptor): IrValueParameter = with(descriptor) {
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
factory.createValueParameter(
function.startOffset, function.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this),
name, indexOrMinusOne, type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline,
isHidden = false, isAssignable = false
).also {
it.parent = function
}
}
if (copyTypeParameters) {
assert(typeParameters.isEmpty())
copyTypeParamsFromDescriptor(descriptor)
}
dispatchReceiverParameter = descriptor.dispatchReceiverParameter?.let { irValueParameter(it) }
extensionReceiverParameter = descriptor.extensionReceiverParameter?.let { irValueParameter(it) }
if (!overwriteValueParameters)
assert(valueParameters.isEmpty())
valueParameters = descriptor.valueParameters.map { irValueParameter(it) }
}
private fun IrFunction.copyTypeParamsFromDescriptor(descriptor: FunctionDescriptor) {
val newTypeParameters = descriptor.typeParameters.map {
factory.createTypeParameter(
startOffset, endOffset,
SERIALIZABLE_PLUGIN_ORIGIN,
IrTypeParameterSymbolImpl(it),
it.name, it.index, it.isReified, it.variance
).also { typeParameter ->
typeParameter.parent = this
}
}
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
newTypeParameters.forEach { typeParameter ->
typeParameter.superTypes = typeParameter.descriptor.upperBounds.map { it.toIrType() }
}
typeParameters = newTypeParameters
}
}
@@ -29,7 +29,7 @@ class SerializerForEnumsGenerator(
irClass: IrClass,
compilerContext: SerializationPluginContext,
serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator,
) : SerializerIrGenerator(irClass, compilerContext, null, serialInfoJvmGenerator) {
) : SerializerIrGenerator(irClass, compilerContext, null) {
override fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc ->
fun irThis(): IrExpression =
IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol)
@@ -23,8 +23,7 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
class SerializerForInlineClassGenerator(
irClass: IrClass,
compilerContext: SerializationPluginContext,
serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator,
) : SerializerIrGenerator(irClass, compilerContext, null, serialInfoJvmGenerator) {
) : SerializerIrGenerator(irClass, compilerContext, null) {
override fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc ->
fun irThis(): IrExpression =
IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol)
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.backend.jvm.functionByName
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.codegen.CompilationException
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
@@ -41,6 +40,7 @@ import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPlug
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_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.LOAD
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SAVE
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_DECODER_CLASS
@@ -55,14 +55,13 @@ open class SerializerIrGenerator(
val irClass: IrClass,
compilerContext: SerializationPluginContext,
metadataPlugin: SerializationDescriptorSerializerPlugin?,
private val serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator,
) : BaseIrGenerator(irClass, compilerContext) {
protected val serializableIrClass = getSerializableClassDescriptorBySerializer(irClass)!!
protected val serialName: String = serializableIrClass.serialName()
protected val properties = serializablePropertiesForIrBackend(serializableIrClass, metadataPlugin)
protected val serializableProperties = properties.serializableProperties
protected val isGeneratedSerializer = irClass.descriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) // TODO TODO
protected val isGeneratedSerializer = irClass.superTypes.any(IrType::isGeneratedKSerializer)
protected val generatedSerialDescPropertyDescriptor = getProperty(
SerialEntityNames.SERIAL_DESC_FIELD,
@@ -71,7 +70,7 @@ open class SerializerIrGenerator(
protected val anySerialDescProperty = getProperty(
SerialEntityNames.SERIAL_DESC_FIELD,
) { true } // TODO REMOVE TRUE
) { true } // remove true?
protected val irAnySerialDescProperty = anySerialDescProperty
@@ -82,18 +81,19 @@ open class SerializerIrGenerator(
return irClass.properties.singleOrNull { it.name.asString() == name && isReturnTypeOk(it) }
}
var localSerializersFieldsDescriptors: List<Pair<PropertyDescriptor, IrProperty>> = emptyList()
protected set
protected fun findLocalSerializersFieldDescriptors(): List<IrProperty> {
var localSerializersFieldsDescriptors: List<IrProperty> = emptyList()
private set
// null if was not found — we're in FIR
private fun findLocalSerializersFieldDescriptors(): List<IrProperty?> {
val count = serializableIrClass.typeParameters.size
if (count == 0) return emptyList()
val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" }
return propNames.mapNotNull { name ->
return propNames.map { name ->
getProperty(name) { it.getter!!.returnType.isKSerializer() }
}
}
protected open val serialDescImplClass: IrClassSymbol =
compilerContext.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL)
@@ -106,11 +106,13 @@ open class SerializerIrGenerator(
// how to (auto)create backing field and getter/setter?
compilerContext.symbolTable.withReferenceScope(irClass) {
prop = generateSimplePropertyWithBackingField(desc.descriptor, irClass)
prop = generatePropertyMissingParts(desc, desc.name, serialDescImplClass.starProjectedType, irClass, desc.visibility)
// TODO: Do not use descriptors here
localSerializersFieldsDescriptors = findLocalSerializersFieldDescriptors().map { prop ->
prop.descriptor to generateSimplePropertyWithBackingField(prop.descriptor, irClass)
localSerializersFieldsDescriptors = findLocalSerializersFieldDescriptors().mapIndexed { i, prop ->
generatePropertyMissingParts(
prop, Name.identifier("${SerialEntityNames.typeArgPrefix}$i"),
compilerContext.getClassFromRuntime(KSERIALIZER_CLASS).starProjectedType, irClass
)
}
}
@@ -141,7 +143,8 @@ open class SerializerIrGenerator(
// save local descriptor to field
+irSetField(
generateReceiverExpressionForFieldAccess(
IrGetValueImpl(
startOffset, endOffset,
thisAsReceiverParameter.symbol
),
prop.backingField!!,
@@ -218,11 +221,9 @@ open class SerializerIrGenerator(
// store type arguments serializers in fields
val thisAsReceiverParameter = irClass.thisReceiver!!
ctor.valueParameters.forEachIndexed { index, param ->
val localSerial = localSerializersFieldsDescriptors[index].second.backingField!!
val localSerial = localSerializersFieldsDescriptors[index].backingField!!
+irSetField(
generateReceiverExpressionForFieldAccess(
thisAsReceiverParameter.symbol
), localSerial, irGet(param)
IrGetValueImpl(startOffset, endOffset, thisAsReceiverParameter.symbol), localSerial, irGet(param)
)
}
}
@@ -243,7 +244,7 @@ open class SerializerIrGenerator(
val typeParams = serializableIrClass.typeParameters.mapIndexed { idx, _ ->
irGetField(
irGet(irFun.dispatchReceiverParameter!!),
localSerializersFieldsDescriptors[idx].second.backingField!!
localSerializersFieldsDescriptors[idx].backingField!!
)
}
val kSerType = ((irFun.returnType as IrSimpleType).arguments.first() as IrTypeProjection).type
@@ -268,7 +269,8 @@ open class SerializerIrGenerator(
val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc")
// fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): StructureEncoder
val beginFunc = encoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 }
val beginFunc =
encoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 }
val call = irCall(beginFunc, type = kOutputClass.defaultType).mapValueParametersIndexed { _, _ ->
irGet(localSerialDesc)
@@ -283,9 +285,9 @@ open class SerializerIrGenerator(
if (writeSelfFunction != null) {
// extract Tx from KSerializer<Tx> list
val typeArgs =
localSerializersFieldsDescriptors.map { (_, ir) -> ir.backingField!!.type.cast<IrSimpleType>().arguments.single().typeOrNull }
localSerializersFieldsDescriptors.map { ir -> ir.backingField!!.type.cast<IrSimpleType>().arguments.single().typeOrNull }
val args = mutableListOf<IrExpression>(irGet(objectToSerialize), irGet(localOutput), irGet(localSerialDesc))
args.addAll(localSerializersFieldsDescriptors.map { (_, ir) ->
args.addAll(localSerializersFieldsDescriptors.map { ir ->
irGetField(
irGet(saveFunc.dispatchReceiverParameter!!),
ir.backingField!!
@@ -304,7 +306,7 @@ open class SerializerIrGenerator(
serializableProperties, objectToSerialize, localOutput,
localSerialDesc, kOutputClass, ignoreIndexTo = -1, initializerAdapter
) { it, _ ->
val (_, ir) = localSerializersFieldsDescriptors[it]
val ir = localSerializersFieldsDescriptors[it]
irGetField(irGet(saveFunc.dispatchReceiverParameter!!), ir.backingField!!)
}
}
@@ -327,7 +329,7 @@ open class SerializerIrGenerator(
whenHaveSerializer,
whenDoNot,
{ it, _ ->
val (_, ir) = localSerializersFieldsDescriptors[it]
val ir = localSerializersFieldsDescriptors[it]
irGetField(irGet(dispatchReceiver), ir.backingField!!)
},
returnTypeHint
@@ -401,7 +403,8 @@ open class SerializerIrGenerator(
}
//input = input.beginStructure(...)
val beginFunc = decoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 }
val beginFunc =
decoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 }
val call = irInvoke(
irGet(loadFunc.valueParameters[0]),
beginFunc,
@@ -422,7 +425,7 @@ open class SerializerIrGenerator(
} to listOf(
localSerialDesc.get(), irInt(index), innerSerial, serialPropertiesMap.getValue(property.ir).get()
)
}, {sti ->
}, { sti ->
inputClass.functions.single {
it.owner.name.asString() == "${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}" &&
it.owner.valueParameters.size == 2
@@ -461,7 +464,12 @@ open class SerializerIrGenerator(
// throw exception on unknown field
val excClassRef = compilerContext.referenceConstructors(ClassId(SerializationPackages.packageFqName, Name.identifier(UNKNOWN_FIELD_EXC)))
val excClassRef = compilerContext.referenceConstructors(
ClassId(
SerializationPackages.packageFqName,
Name.identifier(UNKNOWN_FIELD_EXC)
)
)
.single { it.owner.valueParameters.singleOrNull()?.type?.isInt() == true }
+elseBranch(
irThrow(
@@ -594,7 +602,8 @@ open class SerializerIrGenerator(
val save = irClass.findPluginGeneratedMethod(SAVE)?.let { generateSave(it); true } ?: false
val load = irClass.findPluginGeneratedMethod(LOAD)?.let { generateLoad(it); true } ?: false
irClass.findPluginGeneratedMethod(SerialEntityNames.CHILD_SERIALIZERS_GETTER.identifier)?.let { generateChildSerializersGetter(it) }
irClass.findPluginGeneratedMethod(SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER.identifier)?.let { generateTypeParamsSerializersGetter(it) }
irClass.findPluginGeneratedMethod(SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER.identifier)
?.let { generateTypeParamsSerializersGetter(it) }
if (!prop && (save || load))
generateSerialDesc()
if (serializableIrClass.typeParameters.isNotEmpty()) {
@@ -605,7 +614,6 @@ open class SerializerIrGenerator(
}
companion object {
fun generate(
irClass: IrClass,
@@ -620,8 +628,8 @@ open class SerializerIrGenerator(
context,
serialInfoJvmGenerator
)
serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context, serialInfoJvmGenerator)
else -> SerializerIrGenerator(irClass, context, metadataPlugin, serialInfoJvmGenerator)
serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context)
else -> SerializerIrGenerator(irClass, context, metadataPlugin)
}
generator.generate()
irClass.addDefaultConstructorIfAbsent(context)
@@ -6,53 +6,40 @@
package org.jetbrains.kotlinx.serialization.compiler.fir
import org.jetbrains.kotlin.GeneratedDeclarationKey
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.builder.FirSimpleFunctionBuilder
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildPrimaryConstructor
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.origin
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.declarations.builder.buildTypeParameter
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
import org.jetbrains.kotlin.fir.declarations.utils.addDefaultBoundIfNecessary
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.scopes.impl.toConeType
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.fir.types.constructClassLikeType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
// FIXME KT-53096: this has to be shared (copied from plugin example)
@OptIn(SymbolInternals::class)
fun FirDeclarationGenerationExtension.buildConstructor(classId: ClassId, isInner: Boolean, key: GeneratedDeclarationKey): FirConstructor {
fun FirDeclarationGenerationExtension.buildPrimaryConstructor(owner: FirClassSymbol<*>, isInner: Boolean, key: GeneratedDeclarationKey, status: FirDeclarationStatus): FirConstructor {
val classId = owner.classId
val lookupTag = ConeClassLikeLookupTagImpl(classId)
return buildPrimaryConstructor {
moduleData = session.moduleData
origin = key.origin
returnTypeRef = buildResolvedTypeRef {
type = ConeClassLikeTypeImpl(
lookupTag,
emptyArray(),
isNullable = false
)
}
status = FirResolvedDeclarationStatusImpl(
Visibilities.Public,
Modality.FINAL,
EffectiveVisibility.Public
)
returnTypeRef = owner.defaultType().newRef()
this.status = status
symbol = FirConstructorSymbol(classId)
if (isInner && classId.isNestedClass) {
dispatchReceiverType = classId.parentClassId?.let {
@@ -63,4 +50,31 @@ fun FirDeclarationGenerationExtension.buildConstructor(classId: ClassId, isInner
}.also {
it.containingClassForStaticMemberAttr = lookupTag
}
}
fun ConeKotlinType.newRef(): FirResolvedTypeRef = buildResolvedTypeRef {
type = this@newRef
}
fun newSimpleTypeParameter(firSession: FirSession, containingDeclarationSymbol: FirBasedSymbol<*>, name: Name) = buildTypeParameter {
moduleData = firSession.moduleData
origin = SerializationPluginKey.origin
resolvePhase = FirResolvePhase.BODY_RESOLVE
variance = Variance.INVARIANT
this.name = name
symbol = FirTypeParameterSymbol()
this.containingDeclarationSymbol = containingDeclarationSymbol
isReified = false
addDefaultBoundIfNecessary()
}
fun newSimpleValueParameter(firSession: FirSession, typeRef: FirResolvedTypeRef, name: Name) = buildValueParameter {
moduleData = firSession.moduleData
origin = SerializationPluginKey.origin
this.name = name
this.symbol = FirValueParameterSymbol(this.name)
returnTypeRef = typeRef
isCrossinline = false
isNoinline = false
isVararg = false
}
@@ -14,24 +14,22 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol
import org.jetbrains.kotlin.fir.copy
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.builder.*
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.origin
import org.jetbrains.kotlin.fir.declarations.utils.addDefaultBoundIfNecessary
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.toConeType
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.constructStarProjectedType
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeTypeProjection
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.constructClassLikeType
@@ -170,13 +168,24 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
dispatchReceiverType = owner.defaultType()
// TODO: handle serializable objects
val serializableType =
val serializableClassSymbol =
(owner.getContainingDeclarationSymbol(session) as? FirClassSymbol<*>) ?: error("Can't get outer class for $owner")
// TODO: Add value parameters & type parameters for parameterized classes
typeParameters.addAll(serializableClassSymbol.typeParameterSymbols.map { newSimpleTypeParameter(session, symbol, it.name) })
val parametersAsArguments = typeParameters.map { it.toConeType() }.toTypedArray<ConeTypeProjection>()
valueParameters.addAll(List(serializableClassSymbol.typeParameterSymbols.size) { i ->
newSimpleValueParameter(
session,
kSerializerClassId.constructClassLikeType(arrayOf(parametersAsArguments[i]), false).newRef(),
Name.identifier("${SerialEntityNames.typeArgPrefix}$i")
)
})
returnTypeRef = buildResolvedTypeRef {
type = kSerializerClassId.constructClassLikeType(
arrayOf(serializableType.defaultType().toTypeProjection(Variance.INVARIANT)),
arrayOf(serializableClassSymbol.constructType(parametersAsArguments, false)),
isNullable = false
)
}
@@ -217,8 +226,37 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
}
override fun generateConstructors(context: MemberGenerationContext): List<FirConstructorSymbol> {
val constructor = buildConstructor(context.owner.classId, isInner = false, SerializationPluginKey)
return listOf(constructor.symbol)
val owner = context.owner
val defaultObjectConstructor = buildPrimaryConstructor(
owner, isInner = false, SerializationPluginKey, status = FirResolvedDeclarationStatusImpl(
Visibilities.Private,
Modality.FINAL,
EffectiveVisibility.PrivateInClass
)
)
if (owner.name == SerialEntityNames.SERIALIZER_CLASS_NAME && owner.typeParameterSymbols.isNotEmpty()) {
val parameterizedConstructor = buildConstructor {
moduleData = session.moduleData
origin = SerializationPluginKey.origin
returnTypeRef = defaultObjectConstructor.returnTypeRef
symbol = FirConstructorSymbol(owner.classId)
dispatchReceiverType = defaultObjectConstructor.dispatchReceiverType
status = FirResolvedDeclarationStatusImpl(
Visibilities.Private,
Modality.FINAL,
EffectiveVisibility.PrivateInFile // accessed from a companion
)
valueParameters.addAll(owner.typeParameterSymbols.mapIndexed { i, typeParam ->
newSimpleValueParameter(
session,
kSerializerClassId.constructClassLikeType(arrayOf(typeParam.toConeType()), false).newRef(),
Name.identifier("${SerialEntityNames.typeArgPrefix}$i")
)
})
}
return listOf(defaultObjectConstructor.symbol, parameterizedConstructor.symbol)
}
return listOf(defaultObjectConstructor.symbol)
}
fun addSerializerImplClass(
@@ -244,36 +282,19 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
typeParameters.addAll(owner.typeParameterSymbols.map { param ->
buildTypeParameter {
moduleData = session.moduleData
origin = SerializationPluginKey.origin
resolvePhase = FirResolvePhase.BODY_RESOLVE
variance = Variance.INVARIANT
name = param.name
symbol = FirTypeParameterSymbol()
containingDeclarationSymbol = this@buildRegularClass.symbol
isReified = false
addDefaultBoundIfNecessary() // there should be KSerializer but whatever
}
newSimpleTypeParameter(session, symbol, param.name)
})
superTypeRefs += buildResolvedTypeRef {
type = generatedSerializerClassId.constructClassLikeType(arrayOf(owner.constructStarProjectedType()), isNullable = false)
}
val parametersAsArguments = typeParameters.map { it.toConeType() }.toTypedArray<ConeTypeProjection>()
superTypeRefs += generatedSerializerClassId.constructClassLikeType(
arrayOf(
owner.constructType(
parametersAsArguments,
isNullable = false
)
), isNullable = false
).newRef()
}
// TODO: add typed constructor
// val secondaryCtors =
// if (!hasTypeParams)
// emptyList()
// else
// listOf(
// KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor(
// serializerFirClass,
// thisDescriptor,
// typeParameters
// )
// )
// serializerFirClass.secondaryConstructors = secondaryCtors
return serializerFirClass.symbol
}
@@ -31,6 +31,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.kt");
}
@Test
@TestMetadata("classWithGenericParameters.kt")
public void testClassWithGenericParameters() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.kt");
}
@Test
@TestMetadata("defaultProperties.kt")
public void testDefaultProperties() throws Exception {
@@ -30,6 +30,12 @@ public class SerializationFirMembersTestGenerated extends AbstractSerializationF
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.kt");
}
@Test
@TestMetadata("classWithGenericParameters.kt")
public void testClassWithGenericParameters() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithGenericParameters.kt");
}
@Test
@TestMetadata("defaultProperties.kt")
public void testDefaultProperties() throws Exception {
@@ -1,7 +1,7 @@
public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
public final static ListOfUsers$$serializer INSTANCE
public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor
private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor
static void <clinit>() {
NEW (ListOfUsers$$serializer)
@@ -23,8 +23,7 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa
ICONST_0
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor)
PUTSTATIC (ListOfUsers$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;)
PUTSTATIC (ListOfUsers$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
LABEL (L1)
LINENUMBER (13)
RETURN
@@ -164,7 +163,14 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa
LABEL (L1)
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() {
LABEL (L0)
LINENUMBER (12)
GETSTATIC (ListOfUsers$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor)
ARETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder encoder, ListOfUsers value) {
LABEL (L0)
@@ -320,7 +326,7 @@ public final class ListOfUsers : java/lang/Object {
public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
public final static OptionalUser$$serializer INSTANCE
public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor
private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor
static void <clinit>() {
NEW (OptionalUser$$serializer)
@@ -342,8 +348,7 @@ public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serializ
ICONST_1
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor)
PUTSTATIC (OptionalUser$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;)
PUTSTATIC (OptionalUser$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
LABEL (L1)
LINENUMBER (10)
RETURN
@@ -471,7 +476,14 @@ public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serializ
LABEL (L1)
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() {
LABEL (L0)
LINENUMBER (9)
GETSTATIC (OptionalUser$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor)
ARETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder encoder, OptionalUser value) {
LABEL (L0)
@@ -699,7 +711,7 @@ public final class OptionalUser : java/lang/Object {
public final class User$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
public final static User$$serializer INSTANCE
public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor
private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor
static void <clinit>() {
NEW (User$$serializer)
@@ -725,8 +737,7 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in
ICONST_0
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor)
PUTSTATIC (User$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;)
PUTSTATIC (User$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
LABEL (L1)
LINENUMBER (7)
RETURN
@@ -876,7 +887,14 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in
LABEL (L1)
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() {
LABEL (L0)
LINENUMBER (6)
GETSTATIC (User$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor)
ARETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder encoder, User value) {
LABEL (L0)
@@ -1,4 +1,4 @@
// CURIOUS_ABOUT serialize, deserialize, write$Self, childSerializers, <init>, <clinit>
// CURIOUS_ABOUT serialize, deserialize, write$Self, childSerializers, <init>, <clinit>, getDescriptor
// WITH_STDLIB
import kotlinx.serialization.*
@@ -145,7 +145,12 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa
ARETURN
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() {
LABEL (L0)
GETSTATIC (ListOfUsers$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ARETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder encoder, ListOfUsers value) {
LABEL (L0)
@@ -428,7 +433,12 @@ public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serializ
ARETURN
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() {
LABEL (L0)
GETSTATIC (OptionalUser$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ARETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder encoder, OptionalUser value) {
LABEL (L0)
@@ -794,7 +804,12 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in
ARETURN
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() {
LABEL (L0)
GETSTATIC (User$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ARETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder encoder, User value) {
LABEL (L0)
@@ -11,7 +11,7 @@ public final class DelegatedKt : java/lang/Object {
public final class Test$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
public final static Test$$serializer INSTANCE
public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor
private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor
static void <clinit>() {
NEW (Test$$serializer)
@@ -27,8 +27,7 @@ public final class Test$$serializer : java/lang/Object, kotlinx/serialization/in
CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer)
ICONST_0
INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, <init>, (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V)
CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor)
PUTSTATIC (Test$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;)
PUTSTATIC (Test$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
LABEL (L1)
LINENUMBER (13)
RETURN
@@ -1,7 +1,7 @@
public final class Container$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
public final static Container$$serializer INSTANCE
public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor
private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor
static void <clinit>() {
NEW (Container$$serializer)
@@ -23,8 +23,7 @@ public final class Container$$serializer : java/lang/Object, kotlinx/serializati
ICONST_0
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor)
PUTSTATIC (Container$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;)
PUTSTATIC (Container$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
LABEL (L1)
LINENUMBER (19)
RETURN
@@ -479,7 +478,7 @@ public final class Result$Err : Result {
public final class Result$OK$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
public final static Result$OK$$serializer INSTANCE
public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor
private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor
static void <clinit>() {
NEW (Result$OK$$serializer)
@@ -501,8 +500,7 @@ public final class Result$OK$$serializer : java/lang/Object, kotlinx/serializati
ICONST_0
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor)
PUTSTATIC (Result$OK$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;)
PUTSTATIC (Result$OK$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
RETURN
}
@@ -10,7 +10,7 @@ FILE: classWithCompanionObject.kt
public final companion object Companion : R|kotlin/Any| {
public final fun serializer(): R|kotlinx/serialization/KSerializer<SomeClass>|
public constructor(): R|SomeClass.Companion|
private constructor(): R|SomeClass.Companion|
}
@@ -24,7 +24,7 @@ FILE: classWithCompanionObject.kt
public final fun childSerializers(): R|kotlin/Array<kotlinx/serialization/KSerializer<*>>|
public constructor(): R|SomeClass.$serializer|
private constructor(): R|SomeClass.$serializer|
}
@@ -0,0 +1,61 @@
FILE: classWithGenericParameters.kt
@R|kotlinx/serialization/Serializable|() public final class GenericBox<T, V> : R|kotlin/Any| {
public constructor<T, V>(i: R|kotlin/Int|, t: R|T|, vs: R|kotlin/collections/List<V>|): R|GenericBox<T, V>| {
super<R|kotlin/Any|>()
}
public final val i: R|kotlin/Int| = R|<local>/i|
public get(): R|kotlin/Int|
public final val t: R|T| = R|<local>/t|
public get(): R|T|
public final val vs: R|kotlin/collections/List<V>| = R|<local>/vs|
public get(): R|kotlin/collections/List<V>|
public final companion object Companion : R|kotlin/Any| {
public final fun <T, V> serializer(typeSerial0: R|kotlinx/serialization/KSerializer<T>|, typeSerial1: R|kotlinx/serialization/KSerializer<V>|): R|kotlinx/serialization/KSerializer<GenericBox<T, V>>|
private constructor(): R|GenericBox.Companion|
}
public final class $serializer<T, V> : R|kotlinx/serialization/internal/GeneratedSerializer<GenericBox<T, V>>| {
public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|GenericBox<T, V>|): R|kotlin/Unit|
public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|GenericBox<T, V>|
public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor|
public get(): R|kotlinx/serialization/descriptors/SerialDescriptor|
public final fun childSerializers(): R|kotlin/Array<kotlinx/serialization/KSerializer<*>>|
public final fun typeParametersSerializers(): R|kotlin/Array<kotlinx/serialization/KSerializer<*>>|
private constructor(): R|GenericBox.$serializer<T, V>|
private constructor(typeSerial0: R|kotlinx/serialization/KSerializer<T>|, typeSerial1: R|kotlinx/serialization/KSerializer<V>|): R|GenericBox.$serializer<T, V>|
}
}
public final fun box(): R|kotlin/String| {
lval box: R|GenericBox<kotlin/String, kotlin/Boolean>| = R|/GenericBox.GenericBox|<R|kotlin/String|, R|kotlin/Boolean|>(Int(42), String(foo), R|kotlin/collections/listOf|<R|kotlin/Boolean|>(vararg(Boolean(true), Boolean(false))))
lval serial: R|kotlinx/serialization/KSerializer<GenericBox<kotlin/String, kotlin/Boolean>>| = Q|GenericBox|.R|/GenericBox.Companion.serializer|<R|kotlin/String|, R|kotlin/Boolean|>(Q|kotlin/String|.R|kotlinx/serialization/builtins/serializer|(), Q|kotlin/Boolean|.R|kotlinx/serialization/builtins/serializer|())
lval target: R|kotlin/String| = String({"i":42,"t":"foo","vs":[true,false]})
lval s: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|<R|GenericBox<kotlin/String, kotlin/Boolean>|>(R|<local>/serial|, R|<local>/box|)
when () {
!=(R|<local>/target|, R|<local>/s|) -> {
^box <strcat>(String(Incorrect serialization: ), R|<local>/s|)
}
}
lval decoded: R|GenericBox<kotlin/String, kotlin/Boolean>| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString|<R|GenericBox<kotlin/String, kotlin/Boolean>|>(R|<local>/serial|, R|<local>/s|)
when () {
!=(R|<local>/box|.R|SubstitutionOverride</GenericBox.t: R|kotlin/String|>|, R|<local>/decoded|.R|SubstitutionOverride</GenericBox.t: R|kotlin/String|>|) || !=(R|<local>/box|.R|SubstitutionOverride</GenericBox.vs: R|kotlin/collections/List<kotlin/Boolean>|>|, R|<local>/decoded|.R|SubstitutionOverride</GenericBox.vs: R|kotlin/collections/List<kotlin/Boolean>|>|) -> {
^box String(Incorrect deserialization)
}
}
^box String(OK)
}
@@ -0,0 +1,23 @@
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.builtins.*
@Serializable
class GenericBox<T, V>(
val i: Int,
val t: T,
val vs: List<V>
)
fun box(): String {
val box = GenericBox(42, "foo", listOf(true, false))
val serial = GenericBox.serializer(String.serializer(), Boolean.serializer())
val target = """{"i":42,"t":"foo","vs":[true,false]}"""
val s = Json.encodeToString(serial, box)
if (target != s) return "Incorrect serialization: $s"
val decoded = Json.decodeFromString(serial, s)
if (box.t != decoded.t || box.vs != decoded.vs) return "Incorrect deserialization"
return "OK"
}
@@ -27,7 +27,7 @@ FILE: defaultProperties.kt
public final companion object Companion : R|kotlin/Any| {
public final fun serializer(): R|kotlinx/serialization/KSerializer<SomeClass>|
public constructor(): R|SomeClass.Companion|
private constructor(): R|SomeClass.Companion|
}
@@ -41,7 +41,7 @@ FILE: defaultProperties.kt
public final fun childSerializers(): R|kotlin/Array<kotlinx/serialization/KSerializer<*>>|
public constructor(): R|SomeClass.$serializer|
private constructor(): R|SomeClass.$serializer|
}
@@ -19,7 +19,7 @@ FILE: multipleProperties.kt
public final companion object Companion : R|kotlin/Any| {
public final fun serializer(): R|kotlinx/serialization/KSerializer<SomeClass>|
public constructor(): R|SomeClass.Companion|
private constructor(): R|SomeClass.Companion|
}
@@ -33,7 +33,7 @@ FILE: multipleProperties.kt
public final fun childSerializers(): R|kotlin/Array<kotlinx/serialization/KSerializer<*>>|
public constructor(): R|SomeClass.$serializer|
private constructor(): R|SomeClass.$serializer|
}