From 339b152390cd143c3e2f311f8f7d9af49de68cb5 Mon Sep 17 00:00:00 2001 From: "Sergey.Shanshin" Date: Wed, 23 Nov 2022 19:53:44 +0000 Subject: [PATCH] Implemented caching of child serializers Resolves Kotlin/kotlinx.serialization#1918 Merge-request: KT-MR-7578 Merged-by: Sergey Shanshin --- .../compiler/backend/ir/BaseIrGenerator.kt | 118 ++++++++++--- .../backend/ir/IrBuilderWithPluginContext.kt | 165 +++++++++++------- .../ir/SerializableCompanionIrGenerator.kt | 10 +- .../backend/ir/SerializableIrGenerator.kt | 19 +- .../ir/SerializerForInlineClassGenerator.kt | 4 +- .../backend/ir/SerializerIrGenerator.kt | 98 ++++++----- .../SerializationLoweringExtension.kt | 31 +++- .../compiler/resolve/NamingConventions.kt | 2 + .../testData/codegen/Basic.asm.ir.txt | 65 ++++--- .../testData/codegen/Intrinsics.asm.ir.txt | 10 +- .../testData/codegen/Sealed.asm.ir.txt | 90 ++++++---- 11 files changed, 408 insertions(+), 204 deletions(-) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt index a7900dc9c4f..1f98cd058ca 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt @@ -5,17 +5,14 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir +import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder import org.jetbrains.kotlin.backend.common.lower.irIfThen import org.jetbrains.kotlin.backend.jvm.functionByName import org.jetbrains.kotlin.backend.jvm.ir.fileParent import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration -import org.jetbrains.kotlin.ir.declarations.IrValueParameter +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.deepCopyWithVariables import org.jetbrains.kotlin.ir.expressions.IrClassReference import org.jetbrains.kotlin.ir.expressions.IrExpression @@ -29,13 +26,12 @@ import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.name.CallableId -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_NAME_FQ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.contextSerializerId import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.enumSerializerId import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.objectSerializerId @@ -178,6 +174,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override kOutputClass: IrClassSymbol, ignoreIndexTo: Int, initializerAdapter: (IrExpressionBody) -> IrExpression, + cachedChildSerializerByIndex: (Int) -> IrExpression?, genericGetter: ((Int, IrType) -> IrExpression)? ) { @@ -212,6 +209,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override if (it.elementMethodPrefix != "Unit") args.add(property.irGet()) f to args }, + cachedChildSerializerByIndex(index), genericGetter ) @@ -249,11 +247,12 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override property: IrSerializableProperty, whenHaveSerializer: (serializer: IrExpression, sti: IrSerialTypeInfo) -> FunctionWithArgs, whenDoNot: (sti: IrSerialTypeInfo) -> FunctionWithArgs, + cachedSerializer: IrExpression?, genericGetter: ((Int, IrType) -> IrExpression)? = null, returnTypeHint: IrType? = null ): IrExpression { val sti = getIrSerialTypeInfo(property, compilerContext) - val innerSerial = serializerInstance( + val innerSerial = cachedSerializer ?: serializerInstance( sti.serializer, compilerContext, property.type, @@ -305,24 +304,33 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override fun IrBuilderWithScope.serializerTower( generator: SerializerIrGenerator, dispatchReceiverParameter: IrValueParameter, - property: IrSerializableProperty + property: IrSerializableProperty, + cachedSerializer: IrExpression? ): IrExpression? { val nullableSerClass = compilerContext.referenceProperties(SerialEntityNames.wrapIntoNullableCallableId).single() - val serializer = - property.serializableWith(compilerContext) - ?: if (!property.type.isTypeParameter()) generator.findTypeSerializerOrContext( - compilerContext, - property.type - ) else null - return serializerInstance( - serializer, - compilerContext, - property.type, - genericIndex = property.genericIndex - ) { it, _ -> - val ir = generator.localSerializersFieldsDescriptors[it] - irGetField(irGet(dispatchReceiverParameter), ir.backingField!!) - }?.let { expr -> wrapWithNullableSerializerIfNeeded(property.type, expr, nullableSerClass) } + + val serializerExpression = if (cachedSerializer != null) { + cachedSerializer + } else { + val serializerClassSymbol = + property.serializableWith(compilerContext) + ?: if (!property.type.isTypeParameter()) generator.findTypeSerializerOrContext( + compilerContext, + property.type + ) else null + + serializerInstance( + serializerClassSymbol, + compilerContext, + property.type, + genericIndex = property.genericIndex + ) { it, _ -> + val ir = generator.localSerializersFieldsDescriptors[it] + irGetField(irGet(dispatchReceiverParameter), ir.backingField!!) + } + } + + return serializerExpression?.let { expr -> wrapWithNullableSerializerIfNeeded(property.type, expr, nullableSerClass) } } private fun IrBuilderWithScope.wrapWithNullableSerializerIfNeeded( @@ -359,6 +367,66 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override ) } + internal fun IrClass.addCachedChildSerializersProperty(cacheableSerializers: List): IrProperty? { + // if all child serializers are null (non-cacheable) we don't need to create a property + cacheableSerializers.firstOrNull { it != null } ?: return null + + val kSerializerClass = compilerContext.kSerializerClass + ?: throw AssertionError("Serializer class '$KSERIALIZER_NAME_FQ' not found. Check that the kotlinx.serialization runtime is connected correctly") + val kSerializerType = kSerializerClass.typeWith(compilerContext.irBuiltIns.anyType) + val arrayType = compilerContext.irBuiltIns.arrayClass.typeWith(kSerializerType) + + return addValPropertyWithJvmField(arrayType, SerialEntityNames.CACHED_CHILD_SERIALIZERS_PROPERTY_NAME) { + +createArrayOfExpression(kSerializerType, cacheableSerializers.map { it ?: irNull() }) + } + } + + /** + * Factory to getting cached serializers via variable. + * Must be used only in one place because for each factory creates one variable. + * + * Class from [containingClassProducer] used only if [cacheProperty] is not null. + */ + internal fun IrStatementsBuilder<*>.createCacheableChildSerializersFactory( + cacheProperty: IrProperty?, + cacheableSerializers: List, + containingClassProducer: () -> IrClass + ): (Int) -> IrExpression? { + cacheProperty ?: return { null } + + val variable = + irTemporary(irInvoke(irGetObject(containingClassProducer()), cacheProperty.getter!!.symbol), "cached") + + return { index: Int -> + if (cacheableSerializers[index]) { + irInvoke(irGet(variable), compilerContext.arrayValueGetter.symbol, irInt(index)) + } else { + null + } + } + } + + fun IrClass.createCachedChildSerializers( + serializableProperties: List + ): List { + return DeclarationIrBuilder(compilerContext, symbol).run { + serializableProperties.map { cacheableChildSerializerInstance(it) } + } + } + + private fun IrBuilderWithScope.cacheableChildSerializerInstance(property: IrSerializableProperty): IrExpression? { + val serializer = getIrSerialTypeInfo(property, compilerContext).serializer ?: return null + if (serializer.owner.kind == ClassKind.OBJECT) return null + + return serializerInstance( + serializer, + compilerContext, + property.type, + null, + null + ) + } + fun IrBuilderWithScope.serializerInstance( serializerClassOriginal: IrClassSymbol?, pluginContext: SerializationPluginContext, diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt index 6f9c1aed0e2..778a3023914 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt @@ -5,11 +5,9 @@ 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.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.builders.* @@ -23,17 +21,11 @@ import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.impl.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.name.CallableId -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType -import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.synthetic.syntheticVisibility import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext -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 interface IrBuilderWithPluginContext { @@ -61,8 +53,10 @@ interface IrBuilderWithPluginContext { visibility = DescriptorVisibilities.LOCAL origin = SERIALIZATION_PLUGIN_ORIGIN } - function.body = - DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen) + function.body = DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset) { + val expr = addAndGetLastExpression(bodyGen) + +irReturn(expr) + } function.parent = this val f0Type = compilerContext.irBuiltIns.functionN(0) @@ -78,78 +72,105 @@ interface IrBuilderWithPluginContext { ) } - fun createLazyProperty( + fun addLazyValProperty( containingClass: IrClass, targetIrType: IrType, - name: Name, + propertyName: Name, + visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE, initializerBuilder: IrBlockBodyBuilder.() -> Unit ): IrProperty { - val lazySafeModeClassDescriptor = compilerContext.referenceClass(ClassId.topLevel(LAZY_MODE_FQ))!!.owner - val lazyFunctionSymbol = compilerContext.referenceFunctions(CallableId(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("lazy"))).single { - it.owner.valueParameters.size == 2 && it.owner.valueParameters[0].type == lazySafeModeClassDescriptor.defaultType + val lazyIrType = + compilerContext.lazyClass.defaultType.substitute(mapOf(compilerContext.lazyClass.typeParameters[0].symbol to targetIrType)) + + val field = containingClass.factory.buildField { + startOffset = containingClass.startOffset + endOffset = containingClass.endOffset + name = Name.identifier(propertyName.asString() + "\$delegate") + type = lazyIrType + origin = SERIALIZATION_PLUGIN_ORIGIN + isFinal = true + this.visibility = DescriptorVisibilities.PRIVATE + }.also { it.parent = containingClass } + + containingClass.addAnonymousInit { + val enumElement = IrGetEnumValueImpl( + startOffset, + endOffset, + compilerContext.lazyModeClass.defaultType, + compilerContext.lazyModePublicationEnumEntry.symbol + ) + val lambdaExpression = containingClass.createLambdaExpression(targetIrType, initializerBuilder) + val invokeLazyExpr = + irInvoke(null, compilerContext.lazyFunctionSymbol, listOf(targetIrType), listOf(enumElement, lambdaExpression), lazyIrType) + +irSetField(irGet(containingClass.thisReceiver!!), field, invokeLazyExpr) } - val publicationEntryDescriptor = lazySafeModeClassDescriptor.enumEntries().single { it.name == LAZY_PUBLICATION_MODE_NAME } - val lazyIrClass = compilerContext.referenceClass(ClassId.topLevel(LAZY_FQ))!!.owner - val lazyIrType = lazyIrClass.defaultType.substitute(mapOf(lazyIrClass.typeParameters[0].symbol to targetIrType)) - 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( - startOffset, - endOffset, - lazySafeModeClassDescriptor.defaultType, - publicationEntryDescriptor.symbol - ) + val prop = containingClass.addProperty { + startOffset = containingClass.startOffset + endOffset = containingClass.endOffset + name = propertyName + this.visibility = visibility + this.isVar = false + origin = SERIALIZATION_PLUGIN_ORIGIN + }.apply { + field.correspondingPropertySymbol = this.symbol + backingField = field + } - val lambdaExpression = containingClass.createLambdaExpression(targetIrType, initializerBuilder) + val getter = prop.addGetter { + startOffset = containingClass.startOffset + endOffset = containingClass.endOffset + returnType = targetIrType + origin = SERIALIZATION_PLUGIN_ORIGIN + this.visibility = visibility + modality = Modality.FINAL + } - irExprBody( - irInvoke(null, lazyFunctionSymbol, listOf(targetIrType), listOf(enumElement, lambdaExpression), lazyIrType) + getter.apply { + dispatchReceiverParameter = containingClass.thisReceiver!!.copyTo(this, type = containingClass.defaultType) + body = compilerContext.irBuiltIns.createIrBuilder(symbol, containingClass.startOffset, containingClass.endOffset).irBlockBody { + +irReturn( + irInvoke( + irGetField(irGet(dispatchReceiverParameter!!), field), + compilerContext.lazyValueGetter, + typeHint = targetIrType + ) ) } - backingField!!.initializer = initializerBody } + return prop } - fun createCompanionValProperty( - companionClass: IrClass, + fun IrClass.addValPropertyWithJvmField( type: IrType, name: Name, + visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE, initializerBuilder: IrBlockBodyBuilder.() -> Unit ): IrProperty { - return generateSimplePropertyWithBackingField(name, type, companionClass).apply { - companionClass.contributeAnonymousInitializer { - val irBlockBody = irBlockBody(startOffset, endOffset, initializerBuilder) - irBlockBody.statements.dropLast(1).forEach { +it } - val expression = irBlockBody.statements.last() as? IrExpression - ?: throw AssertionError("Last statement in property initializer builder is not an a expression") - +irSetField(irGetObject(companionClass), backingField!!, expression) + return generateSimplePropertyWithBackingField(name, type, this, visibility).apply { + val field = backingField!! + addAnonymousInit { + val resultExpression = addAndGetLastExpression(initializerBuilder) + +irSetField(irGet(thisReceiver!!), field, resultExpression) } + + val annotationCtor = compilerContext.jvmFieldClassSymbol.constructors.single { it.owner.isPrimary } + val annotationType = compilerContext.jvmFieldClassSymbol.defaultType + + field.annotations += IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, annotationType, annotationCtor) } } - fun IrClass.contributeAnonymousInitializer(bodyGen: IrBlockBodyBuilder.() -> Unit) { - val symbol = IrAnonymousInitializerSymbolImpl(symbol) - factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also { - it.parent = this - declarations.add(it) - it.body = DeclarationIrBuilder(compilerContext, symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen) - } - } - - fun IrBlockBodyBuilder.getLazyValueExpression(thisParam: IrValueParameter, property: IrProperty, type: IrType): IrExpression { - val lazyIrClass = compilerContext.referenceClass(ClassId.topLevel(LAZY_FQ))!!.owner - val valueGetter = lazyIrClass.getPropertyGetter("value")!! - - val propertyGetter = property.getter!! - - return irInvoke( - irGet(propertyGetter.returnType, irGet(thisParam), propertyGetter.symbol), - valueGetter, - typeHint = type - ) + /** + * Add all statements to the builder, except the last one. + * The last statement should be an expression, it will return as a result + */ + private fun IrStatementsBuilder<*>.addAndGetLastExpression(blockBuilder: IrBlockBodyBuilder.() -> Unit): IrExpression { + val irBlockBody = irBlockBody(startOffset, endOffset, blockBuilder) + irBlockBody.statements.dropLast(1).forEach { +it } + return irBlockBody.statements.last() as? IrExpression + ?: throw AssertionError("Last statement in property initializer builder is not an a expression") } fun IrBuilderWithScope.irInvoke( @@ -207,6 +228,26 @@ interface IrBuilderWithPluginContext { } } + fun IrClass.addAnonymousInit(body: IrBlockBodyBuilder.() -> Unit) { + val anonymousInit = this.run { + val symbol = IrAnonymousInitializerSymbolImpl(symbol) + this.factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also { + it.parent = this + declarations.add(it) + } + } + + anonymousInit.buildWithScope { initIrBody -> + initIrBody.body = + DeclarationIrBuilder( + compilerContext, + initIrBody.symbol, + initIrBody.startOffset, + initIrBody.endOffset + ).irBlockBody(body = body) + } + } + fun IrBuilderWithScope.irBinOp(name: Name, lhs: IrExpression, rhs: IrExpression): IrExpression { val classFqName = (lhs.type as IrSimpleType).classOrNull!!.owner.fqNameWhenAvailable!! val symbol = compilerContext.referenceFunctions(CallableId(ClassId.topLevel(classFqName), name)).single() diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt index c360b15528a..7d15195bfe4 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt @@ -119,16 +119,16 @@ class SerializableCompanionIrGenerator( val targetIrType = kSerializerIrClass.defaultType.substitute(mapOf(kSerializerIrClass.typeParameters[0].symbol to compilerContext.irBuiltIns.anyType)) - val property = createLazyProperty(irClass, targetIrType, SerialEntityNames.CACHED_SERIALIZER_PROPERTY_NAME) { - val expr = serializerInstance( - serializer, compilerContext, serializableIrClass.defaultType + val property = addLazyValProperty(irClass, targetIrType, SerialEntityNames.CACHED_SERIALIZER_PROPERTY_NAME) { + val expr = requireNotNull( + serializerInstance(serializer, compilerContext, serializableIrClass.defaultType) ) patchSerializableClassWithMarkerAnnotation(kSerializerIrClass) - +irReturn(requireNotNull(expr)) + +expr } addFunctionBody(methodDescriptor) { - +irReturn(getLazyValueExpression(it.dispatchReceiverParameter!!, property, targetIrType)) + +irReturn(irInvoke(irGet(it.dispatchReceiverParameter!!), property.getter!!.symbol)) } generateSerializerFactoryIfNeeded(methodDescriptor) } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt index 74402ec6aaa..13361bbedbd 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.backend.common.lower.irThrow import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* @@ -56,6 +57,13 @@ class SerializableIrGenerator( private val IrClass.isInternalSerializable: Boolean get() = kind == ClassKind.CLASS && hasSerializableOrMetaAnnotationWithoutArgs() + private val cachedChildSerializers: List = + irClass.companionObject()!!.createCachedChildSerializers(properties.serializableProperties) + + private val cachedChildSerializersProperty: IrProperty? = + irClass.companionObject()!!.addCachedChildSerializersProperty(cachedChildSerializers) + + fun generateInternalConstructor(constructorDescriptor: IrConstructor) = addFunctionBody(constructorDescriptor) { ctor -> val thiz = irClass.thisReceiver!! @@ -200,10 +208,10 @@ class SerializableIrGenerator( return irGetField(irGetObject(companionObject), property.backingField!!) } - private fun IrBlockBodyBuilder.createCachedDescriptorProperty(companionObject: IrClass): IrProperty { + private fun createCachedDescriptorProperty(companionObject: IrClass): IrProperty { val serialDescIrType = serialDescriptorClass.defaultType - return createCompanionValProperty(companionObject, serialDescIrType, CACHED_DESCRIPTOR_FIELD_NAME) { + return companionObject.addValPropertyWithJvmField(serialDescIrType, CACHED_DESCRIPTOR_FIELD_NAME, DescriptorVisibilities.PUBLIC) { val serialDescVar = irTemporary( getInstantiateDescriptorExpr(), nameHint = "serialDesc" @@ -342,10 +350,15 @@ class SerializableIrGenerator( } } + val cachedChildSerializerByIndex = createCacheableChildSerializersFactory( + cachedChildSerializersProperty, + cachedChildSerializers.map { it != null } + ) { irClass.companionObject()!! } + serializeAllProperties( serializableProperties, objectToSerialize, localOutput, localSerialDesc, kOutputClass, - ignoreIndexTo, initializerAdapter + ignoreIndexTo, initializerAdapter, cachedChildSerializerByIndex, ) { it, _ -> irGet(writeSelfFunction.valueParameters[3 + it]) } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt index ee17dc89ab8..6fbfe746eb8 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt @@ -53,7 +53,7 @@ class SerializerForInlineClassGenerator( encoderClass.functionByName("${CallingConventions.encode}${it.elementMethodPrefix}") val args = if (it.elementMethodPrefix != "Unit") listOf(value) else emptyList() f to args - }) + }, null) val actualEncodeCall = irIfNull(compilerContext.irBuiltIns.unitType, irGet(inlineEncoder), irNull(), elementCall) +actualEncodeCall @@ -77,7 +77,7 @@ class SerializerForInlineClassGenerator( decoderClass.functionByName( "${CallingConventions.decode}${sti.elementMethodPrefix}SerializableValue") to listOf(innerSerial) }, { decoderClass.functionByName("${CallingConventions.decode}${it.elementMethodPrefix}") to listOf() - }, returnTypeHint = inlinedType) + }, null, returnTypeHint = inlinedType) val value = coerceToBox(actualCall, loadFunc.returnType) +irReturn(value) } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index f55ec6f10c5..b6cc568bbca 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir -import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder import org.jetbrains.kotlin.backend.common.lower.irIfThen import org.jetbrains.kotlin.backend.common.lower.irThrow import org.jetbrains.kotlin.backend.jvm.functionByName @@ -24,7 +23,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol -import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.ClassId @@ -34,6 +32,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_CHILD_SERIALIZERS_PROPERTY_NAME 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 @@ -60,9 +59,8 @@ open class SerializerIrGenerator( protected val isGeneratedSerializer = irClass.superTypes.any(IrType::isGeneratedKSerializer) protected val generatedSerialDescPropertyDescriptor = getProperty( - SerialEntityNames.SERIAL_DESC_FIELD, - { true } - )?.takeIf { it.isFromPlugin(compilerContext.afterK2) } + SerialEntityNames.SERIAL_DESC_FIELD + ) { true }?.takeIf { it.isFromPlugin(compilerContext.afterK2) } protected val anySerialDescProperty = getProperty( SerialEntityNames.SERIAL_DESC_FIELD, @@ -80,6 +78,14 @@ open class SerializerIrGenerator( var localSerializersFieldsDescriptors: List = emptyList() private set + // child serializers cached if serializable class is internal + private val cachedChildSerializersProperty = + serializableIrClass.companionObject()?.properties?.singleOrNull { it.name == CACHED_CHILD_SERIALIZERS_PROPERTY_NAME } + + // non-object serializers which can be cached + private val cacheableChildSerializers = + serializableIrClass.createCachedChildSerializers(properties.serializableProperties).map { it != null } + // null if was not found — we're in FIR private fun findLocalSerializersFieldDescriptors(): List { val count = serializableIrClass.typeParameters.size @@ -112,42 +118,29 @@ open class SerializerIrGenerator( } } - val anonymousInit = irClass.run { - val symbol = IrAnonymousInitializerSymbolImpl(symbol) - irClass.factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also { - it.parent = this - declarations.add(it) - } - } + irClass.addAnonymousInit { + val localDesc = irTemporary( + instantiateNewDescriptor(serialDescImplClass, irGet(thisAsReceiverParameter)), + nameHint = "serialDesc" + ) - anonymousInit.buildWithScope { initIrBody -> - compilerContext.symbolTable.withReferenceScope(initIrBody) { - initIrBody.body = - DeclarationIrBuilder(compilerContext, initIrBody.symbol, initIrBody.startOffset, initIrBody.endOffset).irBlockBody { - val localDesc = irTemporary( - instantiateNewDescriptor(serialDescImplClass, irGet(thisAsReceiverParameter)), - nameHint = "serialDesc" - ) + addElementsContentToDescriptor(serialDescImplClass, localDesc, addFuncS) + // add class annotations + copySerialInfoAnnotationsToDescriptor( + collectSerialInfoAnnotations(serializableIrClass), + localDesc, + serialDescImplClass.functionByName(CallingConventions.addClassAnnotation) + ) - addElementsContentToDescriptor(serialDescImplClass, localDesc, addFuncS) - // add class annotations - copySerialInfoAnnotationsToDescriptor( - collectSerialInfoAnnotations(serializableIrClass), - localDesc, - serialDescImplClass.functionByName(CallingConventions.addClassAnnotation) - ) - - // save local descriptor to field - +irSetField( - IrGetValueImpl( - startOffset, endOffset, - thisAsReceiverParameter.symbol - ), - prop.backingField!!, - irGet(localDesc) - ) - } - } + // save local descriptor to field + +irSetField( + IrGetValueImpl( + startOffset, endOffset, + thisAsReceiverParameter.symbol + ), + prop.backingField!!, + irGet(localDesc) + ) } } @@ -225,10 +218,15 @@ open class SerializerIrGenerator( } open fun generateChildSerializersGetter(function: IrSimpleFunction) = addFunctionBody(function) { irFun -> - val allSerializers = serializableProperties.map { + val cachedChildSerializerByIndex = createCacheableChildSerializersFactory( + cachedChildSerializersProperty, + cacheableChildSerializers + ) { serializableIrClass.companionObject()!! } + + val allSerializers = serializableProperties.mapIndexed { index, property -> requireNotNull( - serializerTower(this@SerializerIrGenerator, irFun.dispatchReceiverParameter!!, it) - ) { "Property ${it.name} must have a serializer" } + serializerTower(this@SerializerIrGenerator, irFun.dispatchReceiverParameter!!, property, cachedChildSerializerByIndex(index)) + ) { "Property ${property.name} must have a serializer" } } val kSerType = ((irFun.returnType as IrSimpleType).arguments.first() as IrTypeProjection).type @@ -294,9 +292,14 @@ open class SerializerIrGenerator( val initializerAdapter: (IrExpressionBody) -> IrExpression = createInitializerAdapter(serializableIrClass, propertyByParamReplacer, thisSymbol to { irGet(objectToSerialize) }) + val cachedChildSerializerByIndex = + createCacheableChildSerializersFactory(cachedChildSerializersProperty, cacheableChildSerializers) { + serializableIrClass.companionObject()!! + } + serializeAllProperties( serializableProperties, objectToSerialize, localOutput, - localSerialDesc, kOutputClass, ignoreIndexTo = -1, initializerAdapter + localSerialDesc, kOutputClass, ignoreIndexTo = -1, initializerAdapter, cachedChildSerializerByIndex ) { it, _ -> val ir = localSerializersFieldsDescriptors[it] irGetField(irGet(saveFunc.dispatchReceiverParameter!!), ir.backingField!!) @@ -314,12 +317,14 @@ open class SerializerIrGenerator( property: IrSerializableProperty, whenHaveSerializer: (serializer: IrExpression, sti: IrSerialTypeInfo) -> FunctionWithArgs, whenDoNot: (sti: IrSerialTypeInfo) -> FunctionWithArgs, + cachedSerializer: IrExpression?, returnTypeHint: IrType? = null ): IrExpression = formEncodeDecodePropertyCall( encoder, property, whenHaveSerializer, whenDoNot, + cachedSerializer, { it, _ -> val ir = localSerializersFieldsDescriptors[it] irGetField(irGet(dispatchReceiver), ir.backingField!!) @@ -405,6 +410,11 @@ open class SerializerIrGenerator( ) val localInput = irTemporary(call, "input") + val cachedChildSerializerByIndex = createCacheableChildSerializersFactory( + cachedChildSerializersProperty, + cacheableChildSerializers + ) { serializableIrClass.companionObject()!! } + // prepare all .decodeXxxElement calls val decoderCalls: List> = serializableProperties.mapIndexed { index, property -> @@ -422,7 +432,7 @@ open class SerializerIrGenerator( it.owner.name.asString() == "${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}" && it.owner.valueParameters.size == 2 } to listOf(localSerialDesc.get(), irInt(index)) - }, returnTypeHint = property.type) + }, cachedChildSerializerByIndex(index), returnTypeHint = property.type) // local$i = localInput.decode...(...) +irSet( serialPropertiesMap.getValue(property.ir).symbol, diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt index 48db2ba3a91..ccd07076053 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt @@ -11,25 +11,30 @@ import org.jetbrains.kotlin.backend.common.CompilationException import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.extensions.IrIntrinsicExtension import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext -import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.ir.fileParent import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.util.getPropertyGetter import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlinx.serialization.compiler.backend.ir.* import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializationJvmIrIntrinsicSupport import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages import java.util.concurrent.ConcurrentHashMap @@ -55,6 +60,24 @@ class SerializationPluginContext(baseContext: IrPluginContext, val metadataPlugi internal val copiedStaticWriteSelf: MutableMap = ConcurrentHashMap() + // Kotlin built-in declarations + internal val arrayValueGetter = irBuiltIns.arrayClass.owner.declarations.filterIsInstance() + .single { it.name.asString() == "get" } + + // Kotlin stdlib declarations + internal val jvmFieldClassSymbol = referenceClass(StandardClassIds.Annotations.JvmField)!! + + internal val lazyModeClass = referenceClass(ClassId.topLevel(SerializationDependencies.LAZY_MODE_FQ))!!.owner + internal val lazyModePublicationEnumEntry = + lazyModeClass.enumEntries().single { it.name == SerializationDependencies.LAZY_PUBLICATION_MODE_NAME } + internal val lazyFunctionSymbol = + referenceFunctions(CallableId(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("lazy"))).single { + it.owner.valueParameters.size == 2 && it.owner.valueParameters[0].type == lazyModeClass.defaultType + } + internal val lazyClass = referenceClass(ClassId.topLevel(SerializationDependencies.LAZY_FQ))!!.owner + internal val lazyValueGetter = lazyClass.getPropertyGetter("value")!! + + // serialization runtime declarations internal val enumSerializerFactoryFunc = baseContext.referenceFunctions( CallableId( SerializationPackages.internalPackageFqName, @@ -69,6 +92,12 @@ class SerializationPluginContext(baseContext: IrPluginContext, val metadataPlugi ) ).singleOrNull() + /** + * @return `null` if there is no serialization runtime in the classpath + */ + internal val kSerializerClass = referenceClass(SerialEntityNames.KSERIALIZER_CLASS_ID)?.owner + + // evaluated properties override val runtimeHasEnumSerializerFactoryFunctions = enumSerializerFactoryFunc != null && markedEnumSerializerFactoryFunc != null override fun referenceClassId(classId: ClassId): IrClassSymbol? = referenceClass(classId) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index 45e5a29f646..c1e20943aa9 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -68,6 +68,7 @@ object SerialEntityNames { const val CACHED_DESCRIPTOR_FIELD = "\$cachedDescriptor" const val CACHED_SERIALIZER_PROPERTY = "\$cachedSerializer" + const val CACHED_CHILD_SERIALIZERS_PROPERTY = "\$childSerializers" // classes val KCLASS_NAME_FQ = FqName("kotlin.reflect.KClass") @@ -123,6 +124,7 @@ object SerialEntityNames { val SINGLE_MASK_FIELD_MISSING_FUNC_FQ = SerializationPackages.internalPackageFqName.child(SINGLE_MASK_FIELD_MISSING_FUNC_NAME) val ARRAY_MASK_FIELD_MISSING_FUNC_FQ = SerializationPackages.internalPackageFqName.child(ARRAY_MASK_FIELD_MISSING_FUNC_NAME) val CACHED_SERIALIZER_PROPERTY_NAME = Name.identifier(CACHED_SERIALIZER_PROPERTY) + val CACHED_CHILD_SERIALIZERS_PROPERTY_NAME = Name.identifier(CACHED_CHILD_SERIALIZERS_PROPERTY) val CACHED_DESCRIPTOR_FIELD_NAME = Name.identifier(CACHED_DESCRIPTOR_FIELD) val ENUM_SERIALIZER_FACTORY_FUNC_FQ = SerializationPackages.internalPackageFqName.child(ENUM_SERIALIZER_FACTORY_FUNC_NAME) diff --git a/plugins/kotlinx-serialization/testData/codegen/Basic.asm.ir.txt b/plugins/kotlinx-serialization/testData/codegen/Basic.asm.ir.txt index b67e14de703..1eb96801729 100644 --- a/plugins/kotlinx-serialization/testData/codegen/Basic.asm.ir.txt +++ b/plugins/kotlinx-serialization/testData/codegen/Basic.asm.ir.txt @@ -41,19 +41,18 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa public kotlinx.serialization.KSerializer[] childSerializers() { LABEL (L0) LINENUMBER (12) + INVOKESTATIC (ListOfUsers, access$get$childSerializers$cp, ()[Lkotlinx/serialization/KSerializer;) + ASTORE (1) ICONST_1 ANEWARRAY (kotlinx/serialization/KSerializer) - ASTORE (1) + ASTORE (2) + ALOAD (2) + ICONST_0 ALOAD (1) ICONST_0 - NEW (kotlinx/serialization/internal/ArrayListSerializer) - DUP - GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;) - CHECKCAST (kotlinx/serialization/KSerializer) - INVOKESPECIAL (kotlinx/serialization/internal/ArrayListSerializer, , (Lkotlinx/serialization/KSerializer;)V) - CHECKCAST (kotlinx/serialization/KSerializer) + AALOAD AASTORE - ALOAD (1) + ALOAD (2) ARETURN LABEL (L1) } @@ -78,17 +77,17 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa ALOAD (2) INVOKEINTERFACE (kotlinx/serialization/encoding/Decoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;) ASTORE (7) + INVOKESTATIC (ListOfUsers, access$get$childSerializers$cp, ()[Lkotlinx/serialization/KSerializer;) + ASTORE (8) ALOAD (7) INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSequentially, ()Z) IFEQ (L2) ALOAD (7) ALOAD (2) ICONST_0 - NEW (kotlinx/serialization/internal/ArrayListSerializer) - DUP - GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;) - CHECKCAST (kotlinx/serialization/KSerializer) - INVOKESPECIAL (kotlinx/serialization/internal/ArrayListSerializer, , (Lkotlinx/serialization/KSerializer;)V) + ALOAD (8) + ICONST_0 + AALOAD CHECKCAST (kotlinx/serialization/DeserializationStrategy) ALOAD (6) INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;) @@ -118,11 +117,9 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa ALOAD (7) ALOAD (2) ICONST_0 - NEW (kotlinx/serialization/internal/ArrayListSerializer) - DUP - GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;) - CHECKCAST (kotlinx/serialization/KSerializer) - INVOKESPECIAL (kotlinx/serialization/internal/ArrayListSerializer, , (Lkotlinx/serialization/KSerializer;)V) + ALOAD (8) + ICONST_0 + AALOAD CHECKCAST (kotlinx/serialization/DeserializationStrategy) ALOAD (6) INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;) @@ -237,6 +234,8 @@ public final class ListOfUsers$Companion : java/lang/Object { } public final class ListOfUsers : java/lang/Object { + private final static kotlinx.serialization.KSerializer[] $childSerializers + public final static ListOfUsers$Companion Companion private final java.util.List list @@ -247,6 +246,24 @@ public final class ListOfUsers : java/lang/Object { ACONST_NULL INVOKESPECIAL (ListOfUsers$Companion, , (Lkotlin/jvm/internal/DefaultConstructorMarker;)V) PUTSTATIC (ListOfUsers, Companion, LListOfUsers$Companion;) + LABEL (L0) + LINENUMBER (12) + ICONST_1 + ANEWARRAY (kotlinx/serialization/KSerializer) + ASTORE (0) + ALOAD (0) + ICONST_0 + NEW (kotlinx/serialization/internal/ArrayListSerializer) + DUP + GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;) + CHECKCAST (kotlinx/serialization/KSerializer) + INVOKESPECIAL (kotlinx/serialization/internal/ArrayListSerializer, , (Lkotlinx/serialization/KSerializer;)V) + CHECKCAST (kotlinx/serialization/KSerializer) + AASTORE + ALOAD (0) + PUTSTATIC (ListOfUsers, $childSerializers, [Lkotlinx/serialization/KSerializer;) + LABEL (L1) + LINENUMBER (13) RETURN } @@ -291,19 +308,21 @@ public final class ListOfUsers : java/lang/Object { LABEL (L2) } + public final static kotlinx.serialization.KSerializer[] access$get$childSerializers$cp() + public final java.util.List getList() public final static void write$Self(ListOfUsers self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) { LABEL (L0) LINENUMBER (12) + GETSTATIC (ListOfUsers, $childSerializers, [Lkotlinx/serialization/KSerializer;) + ASTORE (3) ALOAD (1) ALOAD (2) ICONST_0 - NEW (kotlinx/serialization/internal/ArrayListSerializer) - DUP - GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;) - CHECKCAST (kotlinx/serialization/KSerializer) - INVOKESPECIAL (kotlinx/serialization/internal/ArrayListSerializer, , (Lkotlinx/serialization/KSerializer;)V) + ALOAD (3) + ICONST_0 + AALOAD CHECKCAST (kotlinx/serialization/SerializationStrategy) ALOAD (0) GETFIELD (ListOfUsers, list, Ljava/util/List;) diff --git a/plugins/kotlinx-serialization/testData/codegen/Intrinsics.asm.ir.txt b/plugins/kotlinx-serialization/testData/codegen/Intrinsics.asm.ir.txt index 123d9ab58c5..7d84fdf36e4 100644 --- a/plugins/kotlinx-serialization/testData/codegen/Intrinsics.asm.ir.txt +++ b/plugins/kotlinx-serialization/testData/codegen/Intrinsics.asm.ir.txt @@ -29,8 +29,6 @@ public final class Box$Companion : java/lang/Object { public void (kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) - private final kotlinx.serialization.descriptors.SerialDescriptor get$cachedDescriptor() - public final kotlinx.serialization.KSerializer serializer(kotlinx.serialization.KSerializer typeSerial0) } @@ -47,8 +45,6 @@ public final class Box : java/lang/Object { public void (int seen1, java.lang.Object boxed, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) - public final static kotlinx.serialization.descriptors.SerialDescriptor access$get$cachedDescriptor$cp() - public final java.lang.Object component1() public final Box copy(java.lang.Object boxed) @@ -191,8 +187,8 @@ public final class IntrinsicsKt : java/lang/Object { } } -final class SerializableObject$$cachedSerializer$delegate$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 { - public final static SerializableObject$$cachedSerializer$delegate$1 INSTANCE +final class SerializableObject$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 { + public final static SerializableObject$1 INSTANCE static void () @@ -212,7 +208,7 @@ public final class SerializableObject : java/lang/Object { private void () - private final kotlin.Lazy get$cachedSerializer$delegate() + private final kotlinx.serialization.KSerializer get$cachedSerializer() public final kotlinx.serialization.KSerializer serializer() } diff --git a/plugins/kotlinx-serialization/testData/codegen/Sealed.asm.ir.txt b/plugins/kotlinx-serialization/testData/codegen/Sealed.asm.ir.txt index 04c55809c0b..7403f0a9bb5 100644 --- a/plugins/kotlinx-serialization/testData/codegen/Sealed.asm.ir.txt +++ b/plugins/kotlinx-serialization/testData/codegen/Sealed.asm.ir.txt @@ -41,15 +41,18 @@ public final class Container$$serializer : java/lang/Object, kotlinx/serializati public kotlinx.serialization.KSerializer[] childSerializers() { LABEL (L0) LINENUMBER (18) + INVOKESTATIC (Container, access$get$childSerializers$cp, ()[Lkotlinx/serialization/KSerializer;) + ASTORE (1) ICONST_1 ANEWARRAY (kotlinx/serialization/KSerializer) - ASTORE (1) + ASTORE (2) + ALOAD (2) + ICONST_0 ALOAD (1) ICONST_0 - GETSTATIC (Result, Companion, LResult$Companion;) - INVOKEVIRTUAL (Result$Companion, serializer, ()Lkotlinx/serialization/KSerializer;) + AALOAD AASTORE - ALOAD (1) + ALOAD (2) ARETURN LABEL (L1) } @@ -74,14 +77,17 @@ public final class Container$$serializer : java/lang/Object, kotlinx/serializati ALOAD (2) INVOKEINTERFACE (kotlinx/serialization/encoding/Decoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;) ASTORE (7) + INVOKESTATIC (Container, access$get$childSerializers$cp, ()[Lkotlinx/serialization/KSerializer;) + ASTORE (8) ALOAD (7) INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSequentially, ()Z) IFEQ (L2) ALOAD (7) ALOAD (2) ICONST_0 - GETSTATIC (Result, Companion, LResult$Companion;) - INVOKEVIRTUAL (Result$Companion, serializer, ()Lkotlinx/serialization/KSerializer;) + ALOAD (8) + ICONST_0 + AALOAD CHECKCAST (kotlinx/serialization/DeserializationStrategy) ALOAD (6) INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;) @@ -111,8 +117,9 @@ public final class Container$$serializer : java/lang/Object, kotlinx/serializati ALOAD (7) ALOAD (2) ICONST_0 - GETSTATIC (Result, Companion, LResult$Companion;) - INVOKEVIRTUAL (Result$Companion, serializer, ()Lkotlinx/serialization/KSerializer;) + ALOAD (8) + ICONST_0 + AALOAD CHECKCAST (kotlinx/serialization/DeserializationStrategy) ALOAD (6) INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;) @@ -191,6 +198,8 @@ public final class Container$Companion : java/lang/Object { } public final class Container : java/lang/Object { + private final static kotlinx.serialization.KSerializer[] $childSerializers + public final static Container$Companion Companion private final Result r @@ -201,6 +210,20 @@ public final class Container : java/lang/Object { ACONST_NULL INVOKESPECIAL (Container$Companion, , (Lkotlin/jvm/internal/DefaultConstructorMarker;)V) PUTSTATIC (Container, Companion, LContainer$Companion;) + LABEL (L0) + LINENUMBER (18) + ICONST_1 + ANEWARRAY (kotlinx/serialization/KSerializer) + ASTORE (0) + ALOAD (0) + ICONST_0 + GETSTATIC (Result, Companion, LResult$Companion;) + INVOKEVIRTUAL (Result$Companion, serializer, ()Lkotlinx/serialization/KSerializer;) + AASTORE + ALOAD (0) + PUTSTATIC (Container, $childSerializers, [Lkotlinx/serialization/KSerializer;) + LABEL (L1) + LINENUMBER (19) RETURN } @@ -245,16 +268,21 @@ public final class Container : java/lang/Object { LABEL (L2) } + public final static kotlinx.serialization.KSerializer[] access$get$childSerializers$cp() + public final Result getR() public final static void write$Self(Container self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) { LABEL (L0) LINENUMBER (18) + GETSTATIC (Container, $childSerializers, [Lkotlinx/serialization/KSerializer;) + ASTORE (3) ALOAD (1) ALOAD (2) ICONST_0 - GETSTATIC (Result, Companion, LResult$Companion;) - INVOKEVIRTUAL (Result$Companion, serializer, ()Lkotlinx/serialization/KSerializer;) + ALOAD (3) + ICONST_0 + AALOAD CHECKCAST (kotlinx/serialization/SerializationStrategy) ALOAD (0) GETFIELD (Container, r, LResult;) @@ -264,14 +292,14 @@ public final class Container : java/lang/Object { } } -final class Result$Companion$$cachedSerializer$delegate$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 { - public final static Result$Companion$$cachedSerializer$delegate$1 INSTANCE +final class Result$Companion$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 { + public final static Result$Companion$1 INSTANCE static void () { - NEW (Result$Companion$$cachedSerializer$delegate$1) + NEW (Result$Companion$1) DUP - INVOKESPECIAL (Result$Companion$$cachedSerializer$delegate$1, , ()V) - PUTSTATIC (Result$Companion$$cachedSerializer$delegate$1, INSTANCE, LResult$Companion$$cachedSerializer$delegate$1;) + INVOKESPECIAL (Result$Companion$1, , ()V) + PUTSTATIC (Result$Companion$1, INSTANCE, LResult$Companion$1;) RETURN } @@ -338,7 +366,7 @@ final class Result$Companion$$cachedSerializer$delegate$1 : kotlin/jvm/internal/ LABEL (L0) LINENUMBER (12) ALOAD (0) - INVOKEVIRTUAL (Result$Companion$$cachedSerializer$delegate$1, invoke, ()Lkotlinx/serialization/KSerializer;) + INVOKEVIRTUAL (Result$Companion$1, invoke, ()Lkotlinx/serialization/KSerializer;) ARETURN LABEL (L1) } @@ -362,28 +390,26 @@ public final class Result$Companion : java/lang/Object { LABEL (L1) } - private final kotlin.Lazy get$cachedSerializer$delegate() + private final kotlinx.serialization.KSerializer get$cachedSerializer() public final kotlinx.serialization.KSerializer serializer() { LABEL (L0) LINENUMBER (12) ALOAD (0) - INVOKESPECIAL (Result$Companion, get$cachedSerializer$delegate, ()Lkotlin/Lazy;) - INVOKEINTERFACE (kotlin/Lazy, getValue, ()Ljava/lang/Object;) - CHECKCAST (kotlinx/serialization/KSerializer) + INVOKESPECIAL (Result$Companion, get$cachedSerializer, ()Lkotlinx/serialization/KSerializer;) ARETURN LABEL (L1) } } -final class Result$Err$$cachedSerializer$delegate$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 { - public final static Result$Err$$cachedSerializer$delegate$1 INSTANCE +final class Result$Err$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 { + public final static Result$Err$1 INSTANCE static void () { - NEW (Result$Err$$cachedSerializer$delegate$1) + NEW (Result$Err$1) DUP - INVOKESPECIAL (Result$Err$$cachedSerializer$delegate$1, , ()V) - PUTSTATIC (Result$Err$$cachedSerializer$delegate$1, INSTANCE, LResult$Err$$cachedSerializer$delegate$1;) + INVOKESPECIAL (Result$Err$1, , ()V) + PUTSTATIC (Result$Err$1, INSTANCE, LResult$Err$1;) RETURN } @@ -415,7 +441,7 @@ final class Result$Err$$cachedSerializer$delegate$1 : kotlin/jvm/internal/Lambda LABEL (L0) LINENUMBER (15) ALOAD (0) - INVOKEVIRTUAL (Result$Err$$cachedSerializer$delegate$1, invoke, ()Lkotlinx/serialization/KSerializer;) + INVOKEVIRTUAL (Result$Err$1, invoke, ()Lkotlinx/serialization/KSerializer;) ARETURN LABEL (L1) } @@ -434,7 +460,7 @@ public final class Result$Err : Result { LABEL (L0) LINENUMBER (15) GETSTATIC (kotlin/LazyThreadSafetyMode, PUBLICATION, Lkotlin/LazyThreadSafetyMode;) - GETSTATIC (Result$Err$$cachedSerializer$delegate$1, INSTANCE, LResult$Err$$cachedSerializer$delegate$1;) + GETSTATIC (Result$Err$1, INSTANCE, LResult$Err$1;) CHECKCAST (kotlin/jvm/functions/Function0) INVOKESTATIC (kotlin/LazyKt, lazy, (Lkotlin/LazyThreadSafetyMode;Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;) PUTSTATIC (Result$Err, $cachedSerializer$delegate, Lkotlin/Lazy;) @@ -451,15 +477,13 @@ public final class Result$Err : Result { LABEL (L1) } - private final kotlin.Lazy get$cachedSerializer$delegate() + private final kotlinx.serialization.KSerializer get$cachedSerializer() public final kotlinx.serialization.KSerializer serializer() { LABEL (L0) LINENUMBER (15) ALOAD (0) - INVOKESPECIAL (Result$Err, get$cachedSerializer$delegate, ()Lkotlin/Lazy;) - INVOKEINTERFACE (kotlin/Lazy, getValue, ()Ljava/lang/Object;) - CHECKCAST (kotlinx/serialization/KSerializer) + INVOKESPECIAL (Result$Err, get$cachedSerializer, ()Lkotlinx/serialization/KSerializer;) ARETURN LABEL (L1) } @@ -737,10 +761,12 @@ public abstract class Result : java/lang/Object, X { LABEL (L0) LINENUMBER (12) GETSTATIC (kotlin/LazyThreadSafetyMode, PUBLICATION, Lkotlin/LazyThreadSafetyMode;) - GETSTATIC (Result$Companion$$cachedSerializer$delegate$1, INSTANCE, LResult$Companion$$cachedSerializer$delegate$1;) + GETSTATIC (Result$Companion$1, INSTANCE, LResult$Companion$1;) CHECKCAST (kotlin/jvm/functions/Function0) INVOKESTATIC (kotlin/LazyKt, lazy, (Lkotlin/LazyThreadSafetyMode;Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;) PUTSTATIC (Result, $cachedSerializer$delegate, Lkotlin/Lazy;) + LABEL (L1) + LINENUMBER (16) RETURN }