From bf6dda2d99e3693b96a84cd769e454b03a2b7934 Mon Sep 17 00:00:00 2001 From: Sergey Shanshin Date: Wed, 3 Mar 2021 21:41:37 +0300 Subject: [PATCH] Lazy delegate for serializer in objects, sealed and abstract classes Fixes Kotlin/kotlinx.serialization#585 --- .../backend/common/SerializableCodegen.kt | 1 - .../common/SerializableCompanionCodegen.kt | 16 +- .../compiler/backend/common/TypeUtil.kt | 2 + .../compiler/backend/ir/GeneratorHelpers.kt | 30 +++ .../ir/SerializableCompanionIrGenerator.kt | 99 +++++++++- .../backend/ir/SerializableIrGenerator.kt | 24 +-- .../backend/ir/SerializerForEnumsGenerator.kt | 6 +- .../ir/SerializerForInlineClassGenerator.kt | 14 +- .../backend/ir/SerializerIrGenerator.kt | 34 ++-- .../js/SerializableCompanionJsTranslator.kt | 2 +- .../compiler/backend/jvm/JVMCodegenUtil.kt | 180 +++++++++++++++++- .../backend/jvm/SerializableCodegenImpl.kt | 10 +- .../jvm/SerializableCompanionCodegenImpl.kt | 62 +++++- .../compiler/resolve/NamingConventions.kt | 3 +- .../compiler/resolve/SearchUtils.kt | 8 + 15 files changed, 423 insertions(+), 68 deletions(-) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt index d81b491452c..44d5ab74c30 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt @@ -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() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt index f3f34da5eae..7684a1d19f5 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt @@ -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) -} \ No newline at end of file + + protected open fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) { + generateSerializerGetter(methodDescriptor) + } +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt index 853d1bee728..e7f9fc72ec5 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt @@ -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 diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt index 2eed5b748e4..063706d30ef 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt @@ -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("") + 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() + + return simpleFunctions.filter { it.name.asString() == functionName }.single { predicate(it) }.symbol + } + fun IrBuilderWithScope.createPrimitiveArrayOfExpression( elementPrimitiveType: IrType, arrayElements: List diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt index 1c451bd6e72..b4b044b2dfa 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt @@ -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( } } -} \ No newline at end of file +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt index 6ce4ca31c27..d65caf3adcd 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt @@ -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() - - return simpleFunctions.filter { it.name.asString() == functionName }.single { predicate(it) }.symbol - } - private fun IrBlockBodyBuilder.generateSuperNonSerializableCall(superClass: IrClass) { val ctorRef = superClass.declarations.filterIsInstance().singleOrNull { it.valueParameters.isEmpty() } ?: error("Non-serializable parent of serializable $serializableDescriptor must have no arg constructor") diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt index 3cfc07836b6..9e431990a74 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt @@ -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) ) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt index e684ad06999..7903b575458 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt @@ -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)) -} \ No newline at end of file +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index f1d8342e60d..7964f76eb6a 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -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() - 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 = 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, diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt index 9f2dcb78123..35ce4ebf1f0 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt @@ -67,4 +67,4 @@ class SerializableCompanionJsTranslator( SerializableCompanionJsTranslator(descriptor, translator, context).generate() } } -} \ No newline at end of file +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt index 02bdd551272..d4f0aed0797 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt @@ -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};>;", + 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, "", "(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, "", "()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 +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt index 98fca4b96f3..3389445db28 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt @@ -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) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt index afc0c95e42f..f10ae20aa79 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt @@ -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};>;", + 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) } } -} \ No newline at end of file +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index 67c02566f98..2ad719718fc 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -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) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SearchUtils.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SearchUtils.kt index 660de3a9e0f..437ab3f2653 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SearchUtils.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SearchUtils.kt @@ -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))