Lazy delegate for serializer in objects, sealed and abstract classes

Fixes Kotlin/kotlinx.serialization#585
This commit is contained in:
Sergey Shanshin
2021-03-03 21:41:37 +03:00
committed by GitHub
parent 89ce629352
commit bf6dda2d99
15 changed files with 423 additions and 68 deletions
@@ -17,7 +17,6 @@ abstract class SerializableCodegen(
bindingContext: BindingContext
) : AbstractSerialGenerator(bindingContext, serializableDescriptor) {
protected val properties = bindingContext.serializablePropertiesFor(serializableDescriptor)
protected val staticDescriptor = serializableDescriptor.declaredTypeParameters.isEmpty()
fun generate() {
generateSyntheticInternalConstructor()
@@ -21,9 +21,8 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_PROVIDER_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion
import org.jetbrains.kotlinx.serialization.compiler.resolve.isKSerializer
abstract class SerializableCompanionCodegen(
protected val companionDescriptor: ClassDescriptor,
@@ -44,8 +43,17 @@ abstract class SerializableCompanionCodegen(
"Can't find synthesized 'Companion.serializer()' function to generate, " +
"probably clash with user-defined function has occurred"
)
generateSerializerGetter(serializerGetterDescriptor)
if (serializableDescriptor.isSerializableObject || serializableDescriptor.isSealedSerializableClass() || serializableDescriptor.isAbstractSerializableClass()) {
generateLazySerializerGetter(serializerGetterDescriptor)
} else {
generateSerializerGetter(serializerGetterDescriptor)
}
}
protected abstract fun generateSerializerGetter(methodDescriptor: FunctionDescriptor)
}
protected open fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) {
generateSerializerGetter(methodDescriptor)
}
}
@@ -101,6 +101,8 @@ fun ClassDescriptor.serialName(): String {
return annotations.serialNameValue ?: fqNameUnsafe.asString()
}
internal val ClassDescriptor.isStaticSerializable: Boolean get() = this.declaredTypeParameters.isEmpty()
/**
* Returns class descriptor for ContextSerializer or PolymorphicSerializer
* if [annotations] contains @Contextual or @Polymorphic annotation
@@ -105,6 +105,26 @@ interface IrBuilderExtension {
return function
}
fun IrClass.createSingletonLambda(
returnType: IrType,
startOffset: Int,
endOffset: Int,
bodyGen: IrBlockBodyBuilder.() -> Unit
): IrSimpleFunction {
val function = compilerContext.irFactory.buildFun {
this.startOffset = startOffset
this.endOffset = endOffset
this.returnType = returnType
name = Name.identifier("<anonymous>")
visibility = DescriptorVisibilities.LOCAL
origin = SERIALIZABLE_PLUGIN_ORIGIN
}
function.body =
DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen)
return function
}
fun IrBuilderWithScope.irInvoke(
dispatchReceiver: IrExpression? = null,
callee: IrFunctionSymbol,
@@ -147,6 +167,16 @@ interface IrBuilderExtension {
}
}
fun ClassDescriptor.referenceFunctionSymbol(
functionName: String,
predicate: (IrSimpleFunction) -> Boolean = { true }
): IrFunctionSymbol {
val irClass = compilerContext.referenceClass(fqNameSafe)?.owner ?: error("Couldn't load class $this")
val simpleFunctions = irClass.declarations.filterIsInstance<IrSimpleFunction>()
return simpleFunctions.filter { it.name.asString() == functionName }.single { predicate(it) }.symbol
}
fun IrBuilderWithScope.createPrimitiveArrayOfExpression(
elementPrimitiveType: IrType,
arrayElements: List<IrExpression>
@@ -7,20 +7,22 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.util.getPropertyGetter
import org.jetbrains.kotlin.ir.util.module
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
@@ -34,6 +36,25 @@ class SerializableCompanionIrGenerator(
bindingContext: BindingContext
) : SerializableCompanionCodegen(irClass.descriptor, bindingContext), IrBuilderExtension {
private val lazyDescriptor = irClass.module.findClassAcrossModuleDependencies(
ClassId(FqName("kotlin"), Name.identifier("Lazy"))
)!!
private val lazySafeModeClassDescriptor = irClass.module.findClassAcrossModuleDependencies(
ClassId(FqName("kotlin"), Name.identifier("LazyThreadSafetyMode"))
)!!
private val lazyFunctionSymbol = compilerContext.referenceFunctions(FqName("kotlin").child(Name.identifier("lazy"))).single {
it.descriptor.valueParameters.size == 2 && it.descriptor.valueParameters[0].type == lazySafeModeClassDescriptor.defaultType
}
private val publicationEntryDescriptor = lazySafeModeClassDescriptor.enumEntries().single { it.name == Name.identifier("PUBLICATION") }
private val function0Descriptor = irClass.module.findClassAcrossModuleDependencies(
ClassId(FqName("kotlin"), Name.identifier("Function0"))
)!!
companion object {
fun generate(
irClass: IrClass,
@@ -84,6 +105,70 @@ class SerializableCompanionIrGenerator(
irSerializableClass.annotations += annotationCtorCall
}
override fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) {
val serializerDescriptor = requireNotNull(
findTypeSerializer(
serializableDescriptor.module,
serializableDescriptor.toSimpleType()
)
)
val field = irClass.addField {
name = Name.identifier(SerialEntityNames.SERIALIZER_LAZY_DELEGATE_FIELD_NAME)
visibility = DescriptorVisibilities.PRIVATE
origin = SERIALIZABLE_PLUGIN_ORIGIN
isFinal = true
isStatic = true
type = lazyDescriptor.defaultType.toIrType()
}.apply {
val lambda = irClass.createSingletonLambda(serializerDescriptor.defaultType.toIrType(), startOffset, endOffset) {
val expr = serializerInstance(
this@SerializableCompanionIrGenerator,
serializerDescriptor, serializableDescriptor.module,
serializableDescriptor.defaultType
)
patchSerializableClassWithMarkerAnnotation(serializerDescriptor)
+irReturn(requireNotNull(expr))
}
val call = IrCallImpl(startOffset, endOffset, type, lazyFunctionSymbol, 0, 2)
call.putValueArgument(
0,
IrGetEnumValueImpl(
startOffset,
endOffset,
publicationEntryDescriptor.classValueType!!.toIrType(),
compilerContext.symbolTable.referenceEnumEntry(publicationEntryDescriptor)
),
)
call.putValueArgument(
1,
IrFunctionExpressionImpl(
startOffset,
endOffset,
function0Descriptor.defaultType.toIrType(),
lambda,
IrStatementOrigin.LAMBDA
),
)
initializer = irClass.factory.createExpressionBody(startOffset, endOffset, call)
}
val valueGetter = compilerContext.referenceClass(lazyDescriptor.fqNameSafe)!!.getPropertyGetter("value")!!
irClass.contributeFunction(methodDescriptor) {
+irReturn(
irGet(
serializerDescriptor.defaultType.toIrType(),
irGetField(null, field), valueGetter
)
)
}
generateSerializerFactoryIfNeeded(methodDescriptor)
}
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) {
irClass.contributeFunction(methodDescriptor) { getter ->
val serializer = requireNotNull(
@@ -138,4 +223,4 @@ class SerializableCompanionIrGenerator(
}
}
}
}
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
@@ -27,13 +26,14 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.getOrPutNullable
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
import org.jetbrains.kotlinx.serialization.compiler.backend.common.isStaticSerializable
import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialName
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.INITIALIZED_DESCRIPTOR_FIELD_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESC_FIELD
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.initializedDescriptorFieldName
class SerializableIrGenerator(
val irClass: IrClass,
@@ -41,7 +41,7 @@ class SerializableIrGenerator(
bindingContext: BindingContext
) : SerializableCodegen(irClass.descriptor, bindingContext), IrBuilderExtension {
private val descriptorGenerationFunctionName = "createInitializedDescriptor"
private val descriptorGenerationFunctionName = Name.identifier("createInitializedDescriptor")
private val serialDescClass: ClassDescriptor = serializableDescriptor.module
.getClassFromSerializationDescriptorsPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS)
@@ -49,7 +49,7 @@ class SerializableIrGenerator(
private val serialDescImplClass: ClassDescriptor = serializableDescriptor
.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL)
private val addElementFun = serialDescImplClass.findFunctionSymbol(CallingConventions.addElement)
private val addElementFun = serialDescImplClass.referenceFunctionSymbol(CallingConventions.addElement)
private fun IrClass.hasSerializableAnnotationWithoutArgs(): Boolean {
val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return false
@@ -170,7 +170,7 @@ class SerializableIrGenerator(
}
private fun IrBlockBodyBuilder.getSerialDescriptorExpr(): IrExpression {
return if (serializableDescriptor.shouldHaveGeneratedSerializer && staticDescriptor) {
return if (serializableDescriptor.isStaticSerializable) {
val serializer = serializableDescriptor.classSerializer!!
val serialDescriptorGetter = compilerContext.referenceClass(serializer.fqNameSafe)!!.getPropertyGetter(SERIAL_DESC_FIELD)!!
irGet(
@@ -187,7 +187,7 @@ class SerializableIrGenerator(
val serialDescItType = serialDescClass.defaultType.toIrType()
val function = irClass.createInlinedFunction(
Name.identifier(descriptorGenerationFunctionName),
descriptorGenerationFunctionName,
DescriptorVisibilities.PRIVATE,
SERIALIZABLE_PLUGIN_ORIGIN,
serialDescItType
@@ -203,7 +203,7 @@ class SerializableIrGenerator(
}
return irClass.addField {
name = Name.identifier(initializedDescriptorFieldName)
name = Name.identifier(INITIALIZED_DESCRIPTOR_FIELD_NAME)
visibility = DescriptorVisibilities.PRIVATE
origin = SERIALIZABLE_PLUGIN_ORIGIN
isFinal = true
@@ -234,16 +234,6 @@ class SerializableIrGenerator(
)
}
private inline fun ClassDescriptor.findFunctionSymbol(
functionName: String,
predicate: (IrSimpleFunction) -> Boolean = { true }
): IrFunctionSymbol {
val irClass = compilerContext.referenceClass(fqNameSafe)?.owner ?: error("Couldn't load class $this")
val simpleFunctions = irClass.declarations.filterIsInstance<IrSimpleFunction>()
return simpleFunctions.filter { it.name.asString() == functionName }.single { predicate(it) }.symbol
}
private fun IrBlockBodyBuilder.generateSuperNonSerializableCall(superClass: IrClass) {
val ctorRef = superClass.declarations.filterIsInstance<IrConstructor>().singleOrNull { it.valueParameters.isEmpty() }
?: error("Non-serializable parent of serializable $serializableDescriptor must have no arg constructor")
@@ -36,7 +36,7 @@ class SerializerForEnumsGenerator(
val encoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.ENCODER_CLASS)
val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol
val encodeEnum = encoderClass.referenceMethod(CallingConventions.encodeEnum)
val encodeEnum = encoderClass.referenceFunctionSymbol(CallingConventions.encodeEnum)
val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol)
val serializableIrClass = requireNotNull(serializableIrClass) { "Enums do not support external serialization" }
@@ -52,7 +52,7 @@ class SerializerForEnumsGenerator(
val decoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.DECODER_CLASS)
val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol
val decode = decoderClass.referenceMethod(CallingConventions.decodeEnum)
val decode = decoderClass.referenceFunctionSymbol(CallingConventions.decodeEnum)
val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol)
val serializableIrClass = requireNotNull(serializableIrClass) { "Enums do not support external serialization" }
@@ -109,7 +109,7 @@ class SerializerForEnumsGenerator(
copySerialInfoAnnotationsToDescriptor(
entry.annotations.mapNotNull(compilerContext.typeTranslator.constantValueGenerator::generateAnnotationConstructorCall),
localDescriptor,
serialDescImplClass.referenceMethod(CallingConventions.addAnnotation)
serialDescImplClass.referenceFunctionSymbol(CallingConventions.addAnnotation)
)
}
}
@@ -32,7 +32,7 @@ class SerializerForInlineClassGenerator(
val encoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.ENCODER_CLASS)
val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol
val encodeInline = encoderClass.referenceMethod(CallingConventions.encodeInline)
val encodeInline = encoderClass.referenceFunctionSymbol(CallingConventions.encodeInline)
val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol)
// val inlineEncoder = encoder.encodeInline()
@@ -45,14 +45,14 @@ class SerializerForInlineClassGenerator(
// inlineEncoder.encodeInt/String/SerializableValue
val elementCall = formEncodeDecodePropertyCall(irGet(inlineEncoder), saveFunc.dispatchReceiverParameter!!, property, {innerSerial, sti ->
val f =
encoderClass.referenceMethod("${CallingConventions.encode}${sti.elementMethodPrefix}SerializableValue")
encoderClass.referenceFunctionSymbol("${CallingConventions.encode}${sti.elementMethodPrefix}SerializableValue")
f to listOf(
innerSerial,
value
)
}, {
val f =
encoderClass.referenceMethod("${CallingConventions.encode}${it.elementMethodPrefix}")
encoderClass.referenceFunctionSymbol("${CallingConventions.encode}${it.elementMethodPrefix}")
val args = if (it.elementMethodPrefix != "Unit") listOf(value) else emptyList()
f to args
})
@@ -67,7 +67,7 @@ class SerializerForInlineClassGenerator(
val decoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.DECODER_CLASS)
val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol
val decodeInline = decoderClass.referenceMethod(CallingConventions.decodeInline)
val decodeInline = decoderClass.referenceFunctionSymbol(CallingConventions.decodeInline)
val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol)
// val inlineDecoder = decoder.decodeInline()
@@ -76,9 +76,9 @@ class SerializerForInlineClassGenerator(
val property = serializableProperties.first()
val inlinedType = property.type.toIrType()
val actualCall = formEncodeDecodePropertyCall(inlineDecoder, loadFunc.dispatchReceiverParameter!!, property, { innerSerial, sti ->
decoderClass.referenceMethod( "${CallingConventions.decode}${sti.elementMethodPrefix}SerializableValue") to listOf(innerSerial)
decoderClass.referenceFunctionSymbol( "${CallingConventions.decode}${sti.elementMethodPrefix}SerializableValue") to listOf(innerSerial)
}, {
decoderClass.referenceMethod("${CallingConventions.decode}${it.elementMethodPrefix}") to listOf()
decoderClass.referenceFunctionSymbol("${CallingConventions.decode}${it.elementMethodPrefix}") to listOf()
}, returnTypeHint = inlinedType)
val value = coerceToBox(actualCall, loadFunc.returnType)
+irReturn(value)
@@ -111,4 +111,4 @@ class SerializerForInlineClassGenerator(
private fun IrBlockBodyBuilder.getFromBox(expression: IrExpression, serializableProperty: SerializableProperty): IrExpression =
getProperty(expression, serializableProperty.getIrPropertyFrom(serializableIrClass))
}
}
@@ -58,7 +58,7 @@ open class SerializerIrGenerator(
override fun generateSerialDesc() {
val desc: PropertyDescriptor = generatedSerialDescPropertyDescriptor ?: return
val addFuncS = serialDescImplClass.referenceMethod(CallingConventions.addElement)
val addFuncS = serialDescImplClass.referenceFunctionSymbol(CallingConventions.addElement)
val thisAsReceiverParameter = irClass.thisReceiver!!
lateinit var prop: IrProperty
@@ -95,7 +95,7 @@ open class SerializerIrGenerator(
copySerialInfoAnnotationsToDescriptor(
serializableIrClass.annotations,
localDesc,
serialDescImplClass.referenceMethod(CallingConventions.addClassAnnotation)
serialDescImplClass.referenceFunctionSymbol(CallingConventions.addClassAnnotation)
)
// save local descriptor to field
@@ -145,7 +145,7 @@ open class SerializerIrGenerator(
copySerialInfoAnnotationsToDescriptor(
property.annotations,
localDescriptor,
serialDescImplClass.referenceMethod(CallingConventions.addAnnotation)
serialDescImplClass.referenceFunctionSymbol(CallingConventions.addAnnotation)
)
}
}
@@ -233,12 +233,6 @@ open class SerializerIrGenerator(
/* Already implemented in .generateSerialClassDesc ? */
}
protected inline fun ClassDescriptor.referenceMethod(methodName: String, predicate: (IrSimpleFunction) -> Boolean = { true }): IrFunctionSymbol {
val irClass = compilerContext.referenceClass(fqNameSafe)?.owner ?: error("Couldn't load class $this")
val simpleFunctions = irClass.declarations.filterIsInstance<IrSimpleFunction>()
return simpleFunctions.filter { it.name.asString() == methodName }.single { predicate(it) }.symbol
}
override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc ->
fun irThis(): IrExpression =
@@ -252,7 +246,7 @@ open class SerializerIrGenerator(
val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc")
// fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): StructureEncoder
val beginFunc = encoderClass.referenceMethod(CallingConventions.begin) { it.valueParameters.size == 1 }
val beginFunc = encoderClass.referenceFunctionSymbol(CallingConventions.begin) { it.valueParameters.size == 1 }
val call = irCall(beginFunc, type = kOutputClass.defaultType.toIrType()).mapValueParametersIndexed { _, _ ->
irGet(localSerialDesc)
@@ -294,7 +288,7 @@ open class SerializerIrGenerator(
// output.writeXxxElementValue(classDesc, index, value)
val elementCall = formEncodeDecodePropertyCall(irGet(localOutput), saveFunc.dispatchReceiverParameter!!, property, {innerSerial, sti ->
val f =
kOutputClass.referenceMethod("${CallingConventions.encode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}")
kOutputClass.referenceFunctionSymbol("${CallingConventions.encode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}")
f to listOf(
irGet(localSerialDesc),
irInt(index),
@@ -303,7 +297,7 @@ open class SerializerIrGenerator(
)
}, {
val f =
kOutputClass.referenceMethod("${CallingConventions.encode}${it.elementMethodPrefix}${CallingConventions.elementPostfix}")
kOutputClass.referenceFunctionSymbol("${CallingConventions.encode}${it.elementMethodPrefix}${CallingConventions.elementPostfix}")
val args: MutableList<IrExpression> = mutableListOf(irGet(localSerialDesc), irInt(index))
if (it.elementMethodPrefix != "Unit") args.add(property.irGet())
f to args
@@ -318,7 +312,7 @@ open class SerializerIrGenerator(
// if ( if (output.shouldEncodeElementDefault(this.descriptor, i)) true else {obj.prop != DEFAULT_VALUE} ) {
// output.encodeIntElement(this.descriptor, i, obj.prop)
// block {obj.prop != DEFAULT_VALUE} may contain several statements
val shouldEncodeFunc = kOutputClass.referenceMethod(CallingConventions.shouldEncodeDefault)
val shouldEncodeFunc = kOutputClass.referenceFunctionSymbol(CallingConventions.shouldEncodeDefault)
val partA = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index))
val partB = irNotEquals(property.irGet(), initializerAdapter(property.irField.initializer!!))
// Ir infrastructure does not have dedicated symbol for ||, so
@@ -329,7 +323,7 @@ open class SerializerIrGenerator(
}
// output.writeEnd(serialClassDesc)
val wEndFunc = kOutputClass.referenceMethod(CallingConventions.end)
val wEndFunc = kOutputClass.referenceFunctionSymbol(CallingConventions.end)
+irInvoke(irGet(localOutput), wEndFunc, irGet(localSerialDesc))
}
@@ -424,7 +418,7 @@ open class SerializerIrGenerator(
}
//input = input.beginStructure(...)
val beginFunc = decoderClass.referenceMethod(CallingConventions.begin) { it.valueParameters.size == 1 }
val beginFunc = decoderClass.referenceFunctionSymbol(CallingConventions.begin) { it.valueParameters.size == 1 }
val call = irInvoke(
irGet(loadFunc.valueParameters[0]),
beginFunc,
@@ -438,13 +432,13 @@ open class SerializerIrGenerator(
serializableProperties.mapIndexed { index, property ->
val body = irBlock {
val decodeFuncToCall = formEncodeDecodePropertyCall(localInput.get(), loadFunc.dispatchReceiverParameter!!, property, {innerSerial, sti ->
inputClass.referenceMethod(
inputClass.referenceFunctionSymbol(
"${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}", {it.valueParameters.size == 4}
) to listOf(
localSerialDesc.get(), irInt(index), innerSerial, serialPropertiesMap.getValue(property.descriptor).get()
)
}, {
inputClass.referenceMethod(
inputClass.referenceFunctionSymbol(
"${CallingConventions.decode}${it.elementMethodPrefix}${CallingConventions.elementPostfix}", {it.valueParameters.size == 2}
) to listOf(
localSerialDesc.get(), irInt(index)
@@ -464,7 +458,7 @@ open class SerializerIrGenerator(
}
// if (decoder.decodeSequentially())
val decodeSequentiallyCall = irInvoke(localInput.get(), inputClass.referenceMethod(CallingConventions.decodeSequentially))
val decodeSequentiallyCall = irInvoke(localInput.get(), inputClass.referenceFunctionSymbol(CallingConventions.decodeSequentially))
val sequentialPart = irBlock {
decoderCalls.forEach { (_, expr) -> +expr.deepCopyWithVariables() }
@@ -473,7 +467,7 @@ open class SerializerIrGenerator(
val byIndexPart: IrExpression = irWhile().also { loop ->
loop.condition = flagVar.get()
loop.body = irBlock {
val readElementF = inputClass.referenceMethod(CallingConventions.decodeElementIndex)
val readElementF = inputClass.referenceFunctionSymbol(CallingConventions.decodeElementIndex)
+irSet(indexVar.symbol, irInvoke(localInput.get(), readElementF, localSerialDesc.get()))
+irWhen {
// if index == -1 (READ_DONE) break loop
@@ -502,7 +496,7 @@ open class SerializerIrGenerator(
+irIfThenElse(compilerContext.irBuiltIns.unitType, decodeSequentiallyCall, sequentialPart, byIndexPart)
//input.endStructure(...)
val endFunc = inputClass.referenceMethod(CallingConventions.end)
val endFunc = inputClass.referenceFunctionSymbol(CallingConventions.end)
+irInvoke(
localInput.get(),
endFunc,
@@ -67,4 +67,4 @@ class SerializableCompanionJsTranslator(
SerializableCompanionJsTranslator(descriptor, translator, context).generate()
}
}
}
}
@@ -7,17 +7,31 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.context.ClassContext
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.load.kotlin.internalName
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
@@ -41,6 +55,7 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UN
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.internalPackageFqName
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.packageFqName
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -55,6 +70,11 @@ internal val decoderType = Type.getObjectType("kotlinx/serialization/encoding/$D
internal val kInputType = Type.getObjectType("kotlinx/serialization/encoding/$STRUCTURE_DECODER_CLASS")
internal val pluginUtilsType = Type.getObjectType("kotlinx/serialization/internal/${PLUGIN_EXCEPTIONS_FILE}Kt")
internal val jvmLambdaType = Type.getObjectType("kotlin/jvm/internal/Lambda")
internal val kotlinLazyType = Type.getObjectType("kotlin/Lazy")
internal val function0Type = Type.getObjectType("kotlin/jvm/functions/Function0")
internal val threadSafeModeType = Type.getObjectType("kotlin/LazyThreadSafetyMode")
internal val kSerialSaverType = Type.getObjectType("kotlinx/serialization/$SERIAL_SAVER_CLASS")
internal val kSerialLoaderType = Type.getObjectType("kotlinx/serialization/$SERIAL_LOADER_CLASS")
internal val kSerializerType = Type.getObjectType("kotlinx/serialization/$KSERIALIZER_CLASS")
@@ -65,7 +85,7 @@ internal val serializationExceptionMissingFieldName = "kotlinx/serialization/$MI
internal val serializationExceptionUnknownIndexName = "kotlinx/serialization/$UNKNOWN_FIELD_EXC"
internal val descriptorGetterName = JvmAbi.getterName(SERIAL_DESC_FIELD)
internal val getLazyValueName = JvmAbi.getterName("value")
val OPT_MASK_TYPE: Type = Type.INT_TYPE
val OPT_MASK_BITS = 32
@@ -485,3 +505,161 @@ fun InstructionAdapter.stackValueDefault(type: Type) {
else -> aconst(null)
}
}
internal fun createSingletonLambda(
lambdaName: String,
outerClassCodegen: ImplementationBodyCodegen,
resultSimpleType: SimpleType,
block: InstructionAdapter.(ImplementationBodyCodegen) -> Unit
): Type {
val lambdaType = Type.getObjectType("${outerClassCodegen.className}\$$lambdaName")
val lambdaClass = ClassDescriptorImpl(
outerClassCodegen.descriptor,
Name.identifier(lambdaName),
Modality.FINAL,
ClassKind.CLASS,
listOf(outerClassCodegen.descriptor.module.builtIns.anyType),
SourceElement.NO_SOURCE,
false,
LockBasedStorageManager.NO_LOCKS
)
lambdaClass.initialize(
MemberScope.Empty, emptySet(),
DescriptorFactory.createPrimaryConstructorForObject(lambdaClass, lambdaClass.source)
)
val lambdaClassBuilder = outerClassCodegen.state.factory.newVisitor(
JvmDeclarationOrigin(JvmDeclarationOriginKind.OTHER, null, lambdaClass),
Type.getObjectType(lambdaType.internalName),
outerClassCodegen.myClass.containingKtFile
)
val classContextForCreator = ClassContext(
outerClassCodegen.typeMapper, lambdaClass, OwnerKind.IMPLEMENTATION, outerClassCodegen.context.parentContext, null
)
val lambdaCodegen = ImplementationBodyCodegen(
outerClassCodegen.myClass,
classContextForCreator,
lambdaClassBuilder,
outerClassCodegen.state,
outerClassCodegen.parentCodegen,
false
)
lambdaCodegen.v.defineClass(
null,
outerClassCodegen.state.classFileVersion,
Opcodes.ACC_FINAL or Opcodes.ACC_SUPER or Opcodes.ACC_SYNTHETIC,
lambdaType.internalName,
"L${jvmLambdaType.internalName};L${function0Type.internalName}<L${kSerializerType.internalName}<*>;>;",
jvmLambdaType.internalName,
arrayOf(function0Type.internalName)
)
outerClassCodegen.v.visitInnerClass(
lambdaType.internalName,
null,
null,
Opcodes.ACC_FINAL or Opcodes.ACC_SUPER or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_STATIC
)
lambdaCodegen.v.visitInnerClass(
lambdaType.internalName,
null,
null,
Opcodes.ACC_FINAL or Opcodes.ACC_SUPER or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_STATIC
)
lambdaCodegen.v.visitOuterClass(
outerClassCodegen.className,
null,
null
)
lambdaCodegen.v.visitSource(
outerClassCodegen.myClass.containingKtFile.name,
null
)
val constr = ClassConstructorDescriptorImpl.createSynthesized(
lambdaClass,
Annotations.EMPTY,
false,
lambdaClass.source
)
constr.initialize(
emptyList(),
DescriptorVisibilities.PUBLIC
)
constr.returnType = lambdaClass.defaultType
lambdaCodegen.generateMethod(constr) { _, _ ->
load(0, lambdaType)
iconst(0)
invokespecial(jvmLambdaType.internalName, "<init>", "(I)V", false)
areturn(Type.VOID_TYPE)
}
lambdaCodegen.v.newField(
OtherOrigin(lambdaCodegen.myClass.psiOrParent),
Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_STATIC or Opcodes.ACC_SYNTHETIC,
JvmAbi.INSTANCE_FIELD,
lambdaType.descriptor,
null,
null
)
val lambdaClInit = lambdaCodegen.createOrGetClInitCodegen()
with(lambdaClInit.v) {
anew(lambdaType)
dup()
invokespecial(lambdaType.internalName, "<init>", "()V", false)
putstatic(lambdaType.internalName, JvmAbi.INSTANCE_FIELD, lambdaType.descriptor)
areturn(Type.VOID_TYPE)
visitEnd()
}
val invokeFunction = SimpleFunctionDescriptorImpl.create(
lambdaCodegen.descriptor,
Annotations.EMPTY,
Name.identifier("invoke"),
CallableMemberDescriptor.Kind.SYNTHESIZED,
lambdaCodegen.descriptor.source
)
invokeFunction.initialize(
null,
lambdaCodegen.descriptor.thisAsReceiverParameter,
emptyList(),
emptyList(),
resultSimpleType,
Modality.FINAL,
DescriptorVisibilities.PUBLIC
)
lambdaCodegen.generateMethod(invokeFunction) { _, _ ->
block(lambdaCodegen)
}
val bridgeInvokeFunction = SimpleFunctionDescriptorImpl.create(
lambdaCodegen.descriptor,
Annotations.EMPTY,
Name.identifier("invoke"),
CallableMemberDescriptor.Kind.SYNTHESIZED,
lambdaCodegen.descriptor.source
)
bridgeInvokeFunction.initialize(
null,
lambdaCodegen.descriptor.thisAsReceiverParameter,
emptyList(),
emptyList(),
lambdaCodegen.descriptor.builtIns.anyType,
Modality.FINAL,
DescriptorVisibilities.PUBLIC
)
lambdaCodegen.generateMethod(bridgeInvokeFunction) { _, _ ->
load(0, lambdaType)
invokevirtual(lambdaType.internalName, "invoke", "()L${resultSimpleType.toClassDescriptor.classId!!.internalName};", false)
areturn(kSerializerType)
}
writeSyntheticClassMetadata(lambdaClassBuilder, lambdaCodegen.state)
lambdaClassBuilder.done()
return lambdaType
}
@@ -35,8 +35,8 @@ import org.jetbrains.kotlinx.serialization.compiler.diagnostic.VersionReader
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.INITIALIZED_DESCRIPTOR_FIELD_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.initializedDescriptorFieldName
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
@@ -347,14 +347,14 @@ class SerializableCodegenImpl(
}
private fun InstructionAdapter.stackSerialDescriptor() {
if (serializableDescriptor.shouldHaveGeneratedSerializer && staticDescriptor) {
if (serializableDescriptor.isStaticSerializable) {
val serializer = serializableDescriptor.classSerializer!!
StackValue.singleton(serializer, classCodegen.typeMapper).put(kSerializerType, this)
invokeinterface(kSerializerType.internalName, descriptorGetterName, "()${descType.descriptor}")
} else {
generateStaticDescriptorField()
getstatic(thisAsmType.internalName, initializedDescriptorFieldName, descType.descriptor)
getstatic(thisAsmType.internalName, INITIALIZED_DESCRIPTOR_FIELD_NAME, descType.descriptor)
}
}
@@ -362,7 +362,7 @@ class SerializableCodegenImpl(
val flags = Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_STATIC
classCodegen.v.newField(
OtherOrigin(classCodegen.myClass.psiOrParent), flags,
initializedDescriptorFieldName, descType.descriptor, null, null
INITIALIZED_DESCRIPTOR_FIELD_NAME, descType.descriptor, null, null
)
val clInit = classCodegen.createOrGetClInitCodegen()
@@ -380,7 +380,7 @@ class SerializableCodegenImpl(
invokevirtual(descImplType.internalName, CallingConventions.addElement, "(Ljava/lang/String;Z)V", false)
}
putstatic(thisAsmType.internalName, initializedDescriptorFieldName, descType.descriptor)
putstatic(thisAsmType.internalName, INITIALIZED_DESCRIPTOR_FIELD_NAME, descType.descriptor)
}
}
@@ -18,12 +18,17 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_LAZY_DELEGATE_FIELD_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.getKSerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion
import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion
import org.jetbrains.kotlinx.serialization.compiler.resolve.toSimpleType
import org.jetbrains.org.objectweb.asm.Opcodes
class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationBodyCodegen) :
SerializableCompanionCodegen(classCodegen.descriptor, classCodegen.bindingContext) {
@@ -36,6 +41,61 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB
}
}
override fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) {
// Create field for lazy delegate
classCodegen.v.newField(
OtherOrigin(classCodegen.myClass.psiOrParent),
Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_STATIC,
SERIALIZER_LAZY_DELEGATE_FIELD_NAME,
kotlinLazyType.descriptor,
"L${kotlinLazyType.internalName}<L${kSerializerType.internalName}<*>;>;",
null
)
// create singleton lambda class
val lambdaType =
createSingletonLambda("serializer\$1", classCodegen, companionDescriptor.getKSerializer().defaultType) { lambdaCodegen ->
val serializerDescriptor = requireNotNull(
findTypeSerializer(
serializableDescriptor.module,
serializableDescriptor.toSimpleType()
)
)
stackValueSerializerInstance(
lambdaCodegen,
serializableDescriptor.module,
serializableDescriptor.defaultType,
serializerDescriptor,
this,
null
)
areturn(kSerializerType)
}
// initialize lazy delegate
val clInit = classCodegen.createOrGetClInitCodegen()
with(clInit.v) {
getstatic(threadSafeModeType.internalName, "PUBLICATION", threadSafeModeType.descriptor)
getstatic(lambdaType.internalName, JvmAbi.INSTANCE_FIELD, lambdaType.descriptor)
checkcast(function0Type)
invokestatic(
"kotlin/LazyKt",
"lazy",
"(${threadSafeModeType.descriptor}${function0Type.descriptor})${kotlinLazyType.descriptor}",
false
)
putstatic(classCodegen.className, SERIALIZER_LAZY_DELEGATE_FIELD_NAME, kotlinLazyType.descriptor)
}
// create serializer getter
classCodegen.generateMethod(methodDescriptor) { _, _ ->
getstatic(classCodegen.className, SERIALIZER_LAZY_DELEGATE_FIELD_NAME, kotlinLazyType.descriptor)
invokeinterface(kotlinLazyType.internalName, getLazyValueName, "()Ljava/lang/Object;")
checkcast(kSerializerType)
areturn(kSerializerType)
}
}
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) {
val serial = requireNotNull(
findTypeSerializer(
@@ -57,4 +117,4 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB
areturn(kSerializerType)
}
}
}
}
@@ -44,7 +44,8 @@ object SerialEntityNames {
const val LOAD = "deserialize"
const val SERIALIZER_CLASS = "\$serializer"
const val initializedDescriptorFieldName = "\$initializedDescriptor"
const val INITIALIZED_DESCRIPTOR_FIELD_NAME = "\$initializedDescriptor"
const val SERIALIZER_LAZY_DELEGATE_FIELD_NAME = "\$serializer\$delegate"
// classes
val KSERIALIZER_NAME = Name.identifier(KSERIALIZER_CLASS)
@@ -69,6 +69,14 @@ internal fun ClassDescriptor.getKSerializerConstructorMarker(): ClassDescriptor
)
)!!
internal fun ClassDescriptor.getKSerializer(): ClassDescriptor =
module.findClassAcrossModuleDependencies(
ClassId(
SerializationPackages.packageFqName,
SerialEntityNames.KSERIALIZER_NAME
)
)!!
internal fun getInternalPackageFqn(classSimpleName: String): FqName =
SerializationPackages.internalPackageFqName.child(Name.identifier(classSimpleName))