From a4b35c58105cce289a7d195b3998a3f85adb65a6 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Thu, 28 Jul 2022 20:21:15 +0200 Subject: [PATCH] Refactor most of the new code, split huge GeneratorHelpers.kt appropriately --- .../backend/common/AbstractSerialGenerator.kt | 48 +- .../common/SerializableCompanionCodegen.kt | 1 - .../compiler/backend/common/TypeUtil.kt | 67 +- .../compiler/backend/ir/BaseIrGenerator.kt | 578 +++++++ .../compiler/backend/ir/DefaultValuesUtils.kt | 126 ++ .../backend/ir/Fir2IrGeneratorUtils.kt | 9 +- .../compiler/backend/ir/GeneratorHelpers.kt | 1367 ----------------- .../backend/ir/IrBuilderWithPluginContext.kt | 569 +++++++ .../compiler/backend/ir/IrPredicates.kt | 202 +++ .../backend/ir/IrSerializableProperties.kt | 100 ++ .../ir/SerialInfoImplJvmIrGenerator.kt | 2 +- .../ir/SerializableCompanionIrGenerator.kt | 62 +- .../backend/ir/SerializableIrGenerator.kt | 26 +- .../backend/ir/SerializerForEnumsGenerator.kt | 11 +- .../ir/SerializerForInlineClassGenerator.kt | 13 +- .../backend/ir/SerializerIrGenerator.kt | 50 +- .../SerializerSearchUtil.kt} | 302 ++-- .../compiler/backend/ir/Synthetics.kt | 39 - .../resolve/SerializableProperties.kt | 101 +- .../compiler/resolve/SerializableProperty.kt | 56 +- .../SerializationIrBoxTestGenerated.java | 3 +- .../testData/codegen/Sealed.ir.txt | 20 +- 22 files changed, 1804 insertions(+), 1948 deletions(-) create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/DefaultValuesUtils.kt delete mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrSerializableProperties.kt rename plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/{common/TypeUtilIr.kt => ir/SerializerSearchUtil.kt} (57%) delete mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Synthetics.kt diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt index b9fab87cca3..ea5ee96a036 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt @@ -5,19 +5,9 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.common -import org.jetbrains.kotlin.backend.jvm.ir.fileParent import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.expressions.IrClassReference -import org.jetbrains.kotlin.ir.expressions.IrVararg -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.isNullable -import org.jetbrains.kotlin.ir.types.typeOrNull -import org.jetbrains.kotlin.ir.util.findAnnotation import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -31,46 +21,15 @@ import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations import org.jetbrains.kotlinx.serialization.compiler.resolve.isKSerializer import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor -abstract class AbstractIrGenerator(private val currentClass: IrClass) { - private fun getClassListFromFileAnnotation(annotationFqName: FqName): List { - val annotation = currentClass.fileParent.annotations.findAnnotation(annotationFqName) ?: return emptyList() - val vararg = annotation.getValueArgument(0) as? IrVararg ?: return emptyList() - return vararg.elements - .mapNotNull { (it as? IrClassReference)?.symbol as? IrClassSymbol} - } - - val contextualKClassListInCurrentFile: Set by lazy { - getClassListFromFileAnnotation( - SerializationAnnotations.contextualFqName, - ).plus( - getClassListFromFileAnnotation( - SerializationAnnotations.contextualOnFileFqName, - ) - ).toSet() - } - - val additionalSerializersInScopeOfCurrentFile: Map, IrClassSymbol> by lazy { - getClassListFromFileAnnotation(SerializationAnnotations.additionalSerializersFqName,) - .associateBy( - { serializerSymbol -> - val kotlinType = (serializerSymbol.owner.superTypes.find(::isKSerializer) as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull - val classSymbol = kotlinType?.classOrNull - ?: throw AssertionError("Argument for ${SerializationAnnotations.additionalSerializersFqName} does not implement KSerializer or does not provide serializer for concrete type") - classSymbol to kotlinType.isNullable() - }, - { it } - ) - } -} abstract class AbstractSerialGenerator(val bindingContext: BindingContext?, val currentDeclaration: ClassDescriptor) { - private fun getKClassListFromFileAnnotation(annotationFqName: FqName, declarationInFile: DeclarationDescriptor): List { - if (bindingContext == null) return emptyList() // TODO: support @UseSerializers in FIR + if (bindingContext == null) return emptyList() val annotation = AnnotationsUtils .getContainingFileAnnotations(bindingContext, declarationInFile) .find { it.fqName == annotationFqName } ?: return emptyList() + @Suppress("UNCHECKED_CAST") val typeList: List = annotation.firstArgument()?.value as? List ?: return emptyList() return typeList.map { it.getArgumentType(declarationInFile.module) } @@ -102,5 +61,6 @@ abstract class AbstractSerialGenerator(val bindingContext: BindingContext?, val } protected fun ClassDescriptor.getFuncDesc(funcName: String): Sequence = - unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) }.asSequence().filterIsInstance() + unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) }.asSequence() + .filterIsInstance() } 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 f0cf7c7d7e3..14fa5653c4a 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 @@ -8,7 +8,6 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.common import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* 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 6bd16a0aa70..fea279e83ee 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 @@ -10,11 +10,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.ir.declarations.IrProperty -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.hasAnnotation -import org.jetbrains.kotlin.ir.util.isTypeParameter import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.name.ClassId @@ -26,32 +21,17 @@ import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.typeUtil.* import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId -import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.internalPackageFqName -interface ISerialTypeInfo> { - val property: S - val elementMethodPrefix: String - val serializer: C? -} - open class SerialTypeInfo( - override val property: SerializableProperty, - override val elementMethodPrefix: String, - override val serializer: ClassDescriptor? = null -) : ISerialTypeInfo - -class IrSerialTypeInfo( - override val property: IrSerializableProperty, - override val elementMethodPrefix: String, - override val serializer: IrClassSymbol? = null -) : ISerialTypeInfo - + val property: SerializableProperty, + val elementMethodPrefix: String, + val serializer: ClassDescriptor? = null +) fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module: ModuleDescriptor): ClassDescriptor? { additionalSerializersInScopeOfCurrentFile[propertyType.toClassDescriptor to propertyType.isMarkedNullable]?.let { return it } @@ -63,48 +43,9 @@ fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module return null } -fun AbstractIrGenerator.findAddOnSerializer(propertyType: IrType, ctx: SerializationPluginContext): IrClassSymbol? { - val classSymbol = propertyType.classOrNull ?: return null - additionalSerializersInScopeOfCurrentFile[classSymbol to propertyType.isNullable()]?.let { return it } - if (classSymbol in contextualKClassListInCurrentFile) - return ctx.getClassFromRuntime(SpecialBuiltins.contextSerializer) - if (classSymbol.owner.annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName)) - return ctx.getClassFromRuntime(SpecialBuiltins.polymorphicSerializer) - if (propertyType.isNullable()) return findAddOnSerializer(propertyType.makeNotNull(), ctx) - return null -} - - fun KotlinType.isGeneratedSerializableObject() = toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs } == true -fun AbstractIrGenerator.getIrSerialTypeInfo(property: IrSerializableProperty, ctx: SerializationPluginContext): IrSerialTypeInfo { - fun SerializableInfo(serializer: IrClassSymbol?) = - IrSerialTypeInfo(property, if (property.type.isNullable()) "Nullable" else "", serializer) - - val T = property.type - property.serializableWith(ctx)?.let { return SerializableInfo(it) } - findAddOnSerializer(T, ctx)?.let { return SerializableInfo(it) } - T.overridenSerializer?.let { return SerializableInfo(it) } - return when { - T.isTypeParameter() -> IrSerialTypeInfo(property, if (property.type.isMarkedNullable()) "Nullable" else "", null) - T.isPrimitiveType() -> IrSerialTypeInfo( - property, - T.classFqName!!.asString().removePrefix("kotlin.") - ) - T.isString() -> IrSerialTypeInfo(property, "String") - T.isArray() -> { - val serializer = property.serializableWith(ctx) ?: ctx.getClassFromInternalSerializationPackage(SpecialBuiltins.referenceArraySerializer) - SerializableInfo(serializer) - } - else -> { - val serializer = - findTypeSerializerOrContext(ctx, property.type) - SerializableInfo(serializer) - } - } -} - @Suppress("FunctionName", "LocalVariableName") fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo { fun SerializableInfo(serializer: ClassDescriptor?) = diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt new file mode 100644 index 00000000000..0a6c9f13d8c --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt @@ -0,0 +1,578 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.serialization.compiler.backend.ir + +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.descriptors.isSealed +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.deepCopyWithVariables +import org.jetbrains.kotlin.ir.expressions.IrClassReference +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrExpressionBody +import org.jetbrains.kotlin.ir.expressions.IrVararg +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol +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.types.Variance +import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.* +import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext +import org.jetbrains.kotlinx.serialization.compiler.resolve.* + +abstract class BaseIrGenerator(private val currentClass: IrClass, final override val compilerContext: SerializationPluginContext): IrBuilderWithPluginContext { + + private val throwMissedFieldExceptionFunc + = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_NAME)).singleOrNull() + + private val throwMissedFieldExceptionArrayFunc + = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME)).singleOrNull() + + private val enumSerializerFactoryFunc + = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME)).singleOrNull() + + private val markedEnumSerializerFactoryFunc + = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME)).singleOrNull() + + fun useFieldMissingOptimization(): Boolean { + return throwMissedFieldExceptionFunc != null && throwMissedFieldExceptionArrayFunc != null + } + + private fun getClassListFromFileAnnotation(annotationFqName: FqName): List { + val annotation = currentClass.fileParent.annotations.findAnnotation(annotationFqName) ?: return emptyList() + val vararg = annotation.getValueArgument(0) as? IrVararg ?: return emptyList() + return vararg.elements + .mapNotNull { (it as? IrClassReference)?.symbol as? IrClassSymbol } + } + + val contextualKClassListInCurrentFile: Set by lazy { + getClassListFromFileAnnotation( + SerializationAnnotations.contextualFqName, + ).plus( + getClassListFromFileAnnotation( + SerializationAnnotations.contextualOnFileFqName, + ) + ).toSet() + } + + val additionalSerializersInScopeOfCurrentFile: Map, IrClassSymbol> by lazy { + getClassListFromFileAnnotation(SerializationAnnotations.additionalSerializersFqName,) + .associateBy( + { serializerSymbol -> + val kotlinType = (serializerSymbol.owner.superTypes.find(IrType::isKSerializer) as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull + val classSymbol = kotlinType?.classOrNull + ?: throw AssertionError("Argument for ${SerializationAnnotations.additionalSerializersFqName} does not implement KSerializer or does not provide serializer for concrete type") + classSymbol to kotlinType.isNullable() + }, + { it } + ) + } + + fun IrBlockBodyBuilder.generateGoldenMaskCheck( + seenVars: List, + properties: IrSerializableProperties, + serialDescriptor: IrExpression + ) { + val fieldsMissedTest: IrExpression + val throwErrorExpr: IrExpression + + val maskSlotCount = seenVars.size + if (maskSlotCount == 1) { + val goldenMask = properties.goldenMask + + + throwErrorExpr = irInvoke( + null, + throwMissedFieldExceptionFunc!!, + irGet(seenVars[0]), + irInt(goldenMask), + serialDescriptor, + typeHint = compilerContext.irBuiltIns.unitType + ) + + fieldsMissedTest = irNotEquals( + irInt(goldenMask), + irBinOp( + OperatorNameConventions.AND, + irInt(goldenMask), + irGet(seenVars[0]) + ) + ) + } else { + val goldenMaskList = properties.goldenMaskList + + var compositeExpression: IrExpression? = null + for (i in goldenMaskList.indices) { + val singleCheckExpr = irNotEquals( + irInt(goldenMaskList[i]), + irBinOp( + OperatorNameConventions.AND, + irInt(goldenMaskList[i]), + irGet(seenVars[i]) + ) + ) + + compositeExpression = if (compositeExpression == null) { + singleCheckExpr + } else { + irBinOp( + OperatorNameConventions.OR, + compositeExpression, + singleCheckExpr + ) + } + } + + fieldsMissedTest = compositeExpression!! + + throwErrorExpr = irBlock { + +irInvoke( + null, + throwMissedFieldExceptionArrayFunc!!, + createPrimitiveArrayOfExpression(compilerContext.irBuiltIns.intType, goldenMaskList.indices.map { irGet(seenVars[it]) }), + createPrimitiveArrayOfExpression(compilerContext.irBuiltIns.intType, goldenMaskList.map { irInt(it) }), + serialDescriptor, + typeHint = compilerContext.irBuiltIns.unitType + ) + } + } + + +irIfThen(compilerContext.irBuiltIns.unitType, fieldsMissedTest, throwErrorExpr) + } + + fun IrBlockBodyBuilder.serializeAllProperties( + serializableProperties: List, + objectToSerialize: IrValueDeclaration, + localOutput: IrValueDeclaration, + localSerialDesc: IrValueDeclaration, + kOutputClass: IrClassSymbol, + ignoreIndexTo: Int, + initializerAdapter: (IrExpressionBody) -> IrExpression, + genericGetter: ((Int, IrType) -> IrExpression)? + ) { + + fun IrSerializableProperty.irGet(): IrExpression { + val ownerType = objectToSerialize.symbol.owner.type + return getProperty( + irGet( + type = ownerType, + variable = objectToSerialize.symbol + ), ir + ) + } + + for ((index, property) in serializableProperties.withIndex()) { + if (index < ignoreIndexTo) continue + // output.writeXxxElementValue(classDesc, index, value) + val elementCall = formEncodeDecodePropertyCall( + irGet(localOutput), + property, { innerSerial, sti -> + val f = + kOutputClass.functionByName("${CallingConventions.encode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}") + f to listOf( + irGet(localSerialDesc), + irInt(index), + innerSerial, + property.irGet() + ) + }, { + val f = + kOutputClass.functionByName("${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 + }, + genericGetter + ) + + // check for call to .shouldEncodeElementDefault + val encodeDefaults = property.ir.getEncodeDefaultAnnotationValue() + val field = + property.ir.backingField // Nullable when property from another module; can't compare it with default value on JS or Native + if (!property.optional || encodeDefaults == true || field == null) { + // emit call right away + +elementCall + } else { + val partB = irNotEquals(property.irGet(), initializerAdapter(field.initializer!!)) + + val condition = if (encodeDefaults == false) { + // drop default without call to .shouldEncodeElementDefault + partB + } else { + // emit check: + // 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.functionByName(CallingConventions.shouldEncodeDefault) + val partA = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index)) + // Ir infrastructure does not have dedicated symbol for ||, so + // `a || b == if (a) true else b`, see org.jetbrains.kotlin.ir.builders.PrimitivesKt.oror + irIfThenElse(compilerContext.irBuiltIns.booleanType, partA, irTrue(), partB) + } + +irIfThen(condition, elementCall) + } + } + } + + + fun IrBlockBodyBuilder.formEncodeDecodePropertyCall( + encoder: IrExpression, + property: IrSerializableProperty, + whenHaveSerializer: (serializer: IrExpression, sti: IrSerialTypeInfo) -> FunctionWithArgs, + whenDoNot: (sti: IrSerialTypeInfo) -> FunctionWithArgs, + genericGetter: ((Int, IrType) -> IrExpression)? = null, + returnTypeHint: IrType? = null + ): IrExpression { + val sti = getIrSerialTypeInfo(property, compilerContext) + val innerSerial = serializerInstance( + sti.serializer, + compilerContext, + property.type, + property.genericIndex, + genericGetter + ) + val (functionToCall, args: List) = if (innerSerial != null) whenHaveSerializer(innerSerial, sti) else whenDoNot(sti) + val typeArgs = if (functionToCall.descriptor.typeParameters.isNotEmpty()) listOf(property.type) else listOf() + return irInvoke(encoder, functionToCall, typeArguments = typeArgs, valueArguments = args, returnTypeHint = returnTypeHint) + } + + fun IrBuilderWithScope.callSerializerFromCompanion( + thisIrType: IrType, + typeArgs: List, + args: List + ): IrExpression? { + val baseClass = thisIrType.getClass() ?: return null + val companionClass = baseClass.companionObject() ?: return null + val serializerProviderFunction = companionClass.declarations.singleOrNull { + it is IrFunction && it.name == SerialEntityNames.SERIALIZER_PROVIDER_NAME && it.valueParameters.size == baseClass.typeParameters.size + } ?: return null + + val adjustedArgs: List = + // if typeArgs.size == args.size then the serializer is custom - we need to use the actual serializers from the arguments + if ((typeArgs.size != args.size) && (baseClass.descriptor.isSealed() || baseClass.descriptor.modality == Modality.ABSTRACT)) { + val serializer = findStandardKotlinTypeSerializer(compilerContext, context.irBuiltIns.unitType)!! + // workaround for sealed and abstract classes - the `serializer` function expects non-null serializers, but does not use them, so serializers of any type can be passed + List(baseClass.typeParameters.size) { irGetObject(serializer) } + } else { + args + } + + with(serializerProviderFunction as IrFunction) { + // Note that [typeArgs] may be unused if we short-cut to e.g. SealedClassSerializer + return irInvoke( + irGetObject(companionClass), + symbol, + typeArgs.takeIf { it.size == typeParameters.size }.orEmpty(), + adjustedArgs.takeIf { it.size == valueParameters.size }.orEmpty() + ) + } + } + + // Does not use sti and therefore does not perform encoder calls optimization + fun IrBuilderWithScope.serializerTower( + generator: SerializerIrGenerator, + dispatchReceiverParameter: IrValueParameter, + property: IrSerializableProperty + ): 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) } + } + + private fun IrBuilderWithScope.wrapWithNullableSerializerIfNeeded( + type: IrType, + expression: IrExpression, + nullableProp: IrPropertySymbol + ): IrExpression = if (type.isMarkedNullable()) { + val resultType = type.makeNotNull() + val typeArguments = listOf(resultType) + val callee = nullableProp.owner.getter!! + + val returnType = callee.returnType.substitute(callee.typeParameters, typeArguments) + + irInvoke( + callee = callee.symbol, + typeArguments = typeArguments, + valueArguments = emptyList(), + returnTypeHint = returnType + ).apply { extensionReceiver = expression } + } else { + expression + } + + fun wrapIrTypeIntoKSerializerIrType( + type: IrType, + variance: Variance = Variance.INVARIANT + ): IrType { + val kSerClass = compilerContext.referenceClass(ClassId(SerializationPackages.packageFqName, SerialEntityNames.KSERIALIZER_NAME)) + ?: error("Couldn't find class ${SerialEntityNames.KSERIALIZER_NAME}") + return IrSimpleTypeImpl( + kSerClass, hasQuestionMark = false, arguments = listOf( + makeTypeProjection(type, variance) + ), annotations = emptyList() + ) + } + + fun IrBuilderWithScope.serializerInstance( + serializerClassOriginal: IrClassSymbol?, + pluginContext: SerializationPluginContext, + kType: IrType, + genericIndex: Int? = null, + genericGetter: ((Int, IrType) -> IrExpression)? = null + ): IrExpression? { + val nullableSerClass = compilerContext.referenceProperties(SerialEntityNames.wrapIntoNullableCallableId).single() + if (serializerClassOriginal == null) { + if (genericIndex == null) return null + return genericGetter?.invoke(genericIndex, kType) + } + if (serializerClassOriginal.owner.kind == ClassKind.OBJECT) { + return irGetObject(serializerClassOriginal) + } + fun instantiate(serializer: IrClassSymbol?, type: IrType): IrExpression? { + val expr = serializerInstance( + serializer, + pluginContext, + type, + type.genericIndex, + genericGetter + ) ?: return null + return wrapWithNullableSerializerIfNeeded(type, expr, nullableSerClass) + } + + var serializerClass = serializerClassOriginal + var args: List + var typeArgs: List + val thisIrType = (kType as? IrSimpleType) ?: error("Don't know how to work with type ${kType::class}") + var needToCopyAnnotations = false + + when (serializerClassOriginal.owner.classId) { + polymorphicSerializerId -> { + needToCopyAnnotations = true + args = listOf(classReference(kType)) + typeArgs = listOf(thisIrType) + } + contextSerializerId -> { + args = listOf(classReference(kType)) + typeArgs = listOf(thisIrType) + + val hasNewCtxSerCtor = compilerContext.referenceConstructors(contextSerializerId).any { it.owner.valueParameters.size == 3 } + + if (hasNewCtxSerCtor) { + // new signature of context serializer + args = args + mutableListOf().apply { + val fallbackDefaultSerializer = findTypeSerializer(pluginContext, kType) + add(instantiate(fallbackDefaultSerializer, kType) ?: irNull()) + add( + createArrayOfExpression( + wrapIrTypeIntoKSerializerIrType( + thisIrType, + variance = Variance.OUT_VARIANCE + ), + thisIrType.arguments.map { + val argSer = findTypeSerializerOrContext( + compilerContext, + it.typeOrNull!! //todo: handle star projections here? + ) + instantiate(argSer, it.typeOrNull!!)!! + }) + ) + } + } + } + objectSerializerId -> { + needToCopyAnnotations = true + args = listOf(irString(kType.serialName()), irGetObject(kType.classOrNull!!)) + typeArgs = listOf(thisIrType) + } + sealedSerializerId -> { + needToCopyAnnotations = true + args = mutableListOf().apply { + add(irString(kType.serialName())) + add(classReference(kType)) + val (subclasses, subSerializers) = allSealedSerializableSubclassesFor( + kType.classOrNull!!.owner, + pluginContext + ) + val projectedOutCurrentKClass = + compilerContext.irBuiltIns.kClassClass.typeWithArguments( + listOf(makeTypeProjection(thisIrType, Variance.OUT_VARIANCE)) + ) + add( + createArrayOfExpression( + projectedOutCurrentKClass, + subclasses.map { classReference(it) } + ) + ) + add( + createArrayOfExpression( + wrapIrTypeIntoKSerializerIrType(thisIrType, variance = Variance.OUT_VARIANCE), + subSerializers.mapIndexed { i, serializer -> + val type = subclasses[i] + val expr = serializerInstance( + serializer, + pluginContext, + type, + type.genericIndex + ) { _, genericType -> + serializerInstance( + pluginContext.referenceClass(polymorphicSerializerId), + pluginContext, + (genericType.classifierOrNull as IrTypeParameterSymbol).owner.representativeUpperBound + )!! + }!! + wrapWithNullableSerializerIfNeeded(type, expr, nullableSerClass) + } + ) + ) + } + typeArgs = listOf(thisIrType) + } + enumSerializerId -> { + serializerClass = pluginContext.referenceClass(enumSerializerId) + val enumDescriptor = kType.classOrNull!! + typeArgs = listOf(thisIrType) + // instantiate serializer only inside enum Companion + if (this@BaseIrGenerator !is SerializableCompanionIrGenerator) { + // otherwise call Companion.serializer() + callSerializerFromCompanion(thisIrType, typeArgs, emptyList())?.let { return it } + } + + val enumArgs = mutableListOf( + irString(thisIrType.serialName()), + irCall(enumDescriptor.owner.findEnumValuesMethod()), + ) + + val enumSerializerFactoryFunc = enumSerializerFactoryFunc + val markedEnumSerializerFactoryFunc = markedEnumSerializerFactoryFunc + if (enumSerializerFactoryFunc != null && markedEnumSerializerFactoryFunc != null) { + // runtime contains enum serializer factory functions + val factoryFunc: IrSimpleFunctionSymbol = if (enumDescriptor.owner.isEnumWithSerialInfoAnnotation()) { + // need to store SerialInfo annotation in descriptor + val enumEntries = enumDescriptor.owner.enumEntries() + val entriesNames = enumEntries.map { it.annotations.serialNameValue?.let { n -> irString(n) } ?: irNull() } + val entriesAnnotations = enumEntries.map { + val annotationConstructors = it.annotations.map { a -> + a.deepCopyWithVariables() + } + val annotationsConstructors = copyAnnotationsFrom(annotationConstructors) + if (annotationsConstructors.isEmpty()) { + irNull() + } else { + createArrayOfExpression(compilerContext.irBuiltIns.annotationType, annotationsConstructors) + } + } + val annotationArrayType = + compilerContext.irBuiltIns.arrayClass.typeWith(compilerContext.irBuiltIns.annotationType.makeNullable()) + + enumArgs += createArrayOfExpression(compilerContext.irBuiltIns.stringType.makeNullable(), entriesNames) + enumArgs += createArrayOfExpression(annotationArrayType, entriesAnnotations) + + markedEnumSerializerFactoryFunc + } else { + enumSerializerFactoryFunc + } + + val factoryReturnType = factoryFunc.owner.returnType.substitute(factoryFunc.owner.typeParameters, typeArgs) + return irInvoke(null, factoryFunc, typeArgs, enumArgs, factoryReturnType) + } else { + // support legacy serializer instantiation by constructor for old runtimes + args = enumArgs + } + } + else -> { + args = kType.arguments.map { + val argSer = findTypeSerializerOrContext( + pluginContext, + it.typeOrNull!! // todo: stars? + ) + instantiate(argSer, it.typeOrNull!!) ?: return null + } + typeArgs = kType.arguments.map { it.typeOrNull!! } + } + + } + if (serializerClassOriginal.owner.classId == referenceArraySerializerId) { + args = listOf(wrapperClassReference(kType.arguments.single().typeOrNull!!)) + args + typeArgs = listOf(typeArgs[0].makeNotNull()) + typeArgs + } + + // If KType is interface, .classSerializer always yields PolymorphicSerializer, which may be unavailable for interfaces from other modules + if (!kType.isInterface() && serializerClassOriginal == kType.classOrNull!!.owner.classSerializer(pluginContext) && this@BaseIrGenerator !is SerializableCompanionIrGenerator) { + // This is default type serializer, we can shortcut through Companion.serializer() + // BUT not during generation of this method itself + callSerializerFromCompanion(thisIrType, typeArgs, args)?.let { return it } + } + + + val serializable = serializerClass?.owner?.let { getSerializableClassDescriptorBySerializer(it) } + requireNotNull(serializerClass) + val ctor = if (serializable?.typeParameters?.isNotEmpty() == true) { + requireNotNull( + findSerializerConstructorForTypeArgumentsSerializers(serializerClass.owner) + ) { "Generated serializer does not have constructor with required number of arguments" } + } else { + val constructors = serializerClass.constructors + // search for new signature of polymorphic/sealed/contextual serializer + if (!needToCopyAnnotations) { + constructors.single { it.owner.isPrimary } + } else { + constructors.find { it.owner.lastArgumentIsAnnotationArray() } ?: run { + // not found - we are using old serialization runtime without this feature + // todo: optimize allocating an empty array when no annotations defined, maybe use old constructor? + needToCopyAnnotations = false + constructors.single { it.owner.isPrimary } + } + } + } + // Return type should be correctly substituted + assert(ctor.isBound) + val ctorDecl = ctor.owner + if (needToCopyAnnotations) { + val classAnnotations = copyAnnotationsFrom(thisIrType.getClass()?.let { collectSerialInfoAnnotations(it) }.orEmpty()) + args = args + createArrayOfExpression(compilerContext.irBuiltIns.annotationType, classAnnotations) + } + + val typeParameters = ctorDecl.parentAsClass.typeParameters + val substitutedReturnType = ctorDecl.returnType.substitute(typeParameters, typeArgs) + return irInvoke( + null, + ctor, + // User may declare serializer with fixed type arguments, e.g. class SomeSerializer : KSerializer> + typeArguments = typeArgs.takeIf { it.size == ctorDecl.typeParameters.size }.orEmpty(), + valueArguments = args.takeIf { it.size == ctorDecl.valueParameters.size }.orEmpty(), + returnTypeHint = substitutedReturnType + ) + } + +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/DefaultValuesUtils.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/DefaultValuesUtils.kt new file mode 100644 index 00000000000..43c07c6ac5a --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/DefaultValuesUtils.kt @@ -0,0 +1,126 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.serialization.compiler.backend.ir + +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope +import org.jetbrains.kotlin.ir.builders.irGet +import org.jetbrains.kotlin.ir.builders.irGetField +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.declarations.IrValueParameter +import org.jetbrains.kotlin.ir.deepCopyWithVariables +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrExpressionBody +import org.jetbrains.kotlin.ir.expressions.IrGetValue +import org.jetbrains.kotlin.ir.symbols.IrValueSymbol +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.properties +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid + +fun IrBuilderWithScope.getProperty(receiver: IrExpression, property: IrProperty): IrExpression { + return if (property.getter != null) + irGet(property.getter!!.returnType, receiver, property.getter!!.symbol) + else + irGetField(receiver, property.backingField!!) +} + +/* + Create a function that creates `get property value expressions` for given corresponded constructor's param + (constructor_params) -> get_property_value_expression + */ +fun IrBuilderWithScope.createPropertyByParamReplacer( + irClass: IrClass, + serialProperties: List, + instance: IrValueParameter +): (ValueParameterDescriptor) -> IrExpression? { + fun IrSerializableProperty.irGet(): IrExpression { + val ownerType = instance.symbol.owner.type + return getProperty( + irGet( + type = ownerType, + variable = instance.symbol + ), ir + ) + } + + val serialPropertiesMap = serialProperties.associateBy { it.ir } + + val transientPropertiesSet = + irClass.declarations.asSequence() + .filterIsInstance() + .filter { it.backingField != null } + .filter { !serialPropertiesMap.containsKey(it) } + .toSet() + + return { vpd -> + val propertyDescriptor = irClass.properties.find { it.name == vpd.name } + if (propertyDescriptor != null) { + val value = serialPropertiesMap[propertyDescriptor] + value?.irGet() ?: run { + if (propertyDescriptor in transientPropertiesSet) + getProperty( + irGet(instance), + propertyDescriptor + ) + else null + } + } else { + null + } + } +} + +/* + Creates an initializer adapter function that can replace IR expressions of getting constructor parameter value by some other expression. + Also adapter may replace IR expression of getting `this` value by another expression. + */ +fun createInitializerAdapter( + irClass: IrClass, + paramGetReplacer: (ValueParameterDescriptor) -> IrExpression?, + thisGetReplacer: Pair IrExpression>? = null +): (IrExpressionBody) -> IrExpression { + val initializerTransformer = object : IrElementTransformerVoid() { + // try to replace `get some value` expression + override fun visitGetValue(expression: IrGetValue): IrExpression { + val symbol = expression.symbol + if (thisGetReplacer != null && thisGetReplacer.first == symbol) { + // replace `get this value` expression + return thisGetReplacer.second() + } + + val descriptor = symbol.descriptor + if (descriptor is ValueParameterDescriptor) { + // replace `get parameter value` expression + paramGetReplacer(descriptor)?.let { return it } + } + + // otherwise leave expression as it is + return super.visitGetValue(expression) + } + } + val defaultsMap = extractDefaultValuesFromConstructor(irClass) + return fun(initializer: IrExpressionBody): IrExpression { + val rawExpression = initializer.expression + val expression = + if (rawExpression.isInitializePropertyFromParameter()) { + // this is a primary constructor property, use corresponding default of value parameter + defaultsMap.getValue((rawExpression as IrGetValue).symbol)!! + } else { + rawExpression + } + return expression.deepCopyWithVariables().transform(initializerTransformer, null) + } +} + +private fun extractDefaultValuesFromConstructor(irClass: IrClass?): Map { + if (irClass == null) return emptyMap() + val original = irClass.constructors.singleOrNull { it.isPrimary } + // default arguments of original constructor + val defaultsMap: Map = + original?.valueParameters?.associate { it.symbol to it.defaultValue?.expression } ?: emptyMap() + return defaultsMap + extractDefaultValuesFromConstructor(irClass.getSuperClassNotAny()) +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Fir2IrGeneratorUtils.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Fir2IrGeneratorUtils.kt index ae9e69a0faa..a2c2a57a388 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Fir2IrGeneratorUtils.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Fir2IrGeneratorUtils.kt @@ -11,8 +11,8 @@ import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.expressions.IrBody import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl -import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.primaryConstructor // TODO KT-53096 @@ -38,4 +38,9 @@ fun IrPluginContext.generateBodyForDefaultConstructor(declaration: IrConstructor return irFactory.createBlockBody(-1, -1, listOf(delegatingAnyCall, initializerCall)) } -val Sequence.primary get() = find { it.isPrimary } ?: error("Expected to have a primary constructor") \ No newline at end of file +val Sequence.primary get() = find { it.isPrimary } ?: error("Expected to have a primary constructor") + +fun IrClass.addDefaultConstructorIfAbsent(ctx: IrPluginContext) { + val declaration = constructors.primary + if (declaration.body == null) declaration.body = ctx.generateBodyForDefaultConstructor(declaration) +} \ No newline at end of file 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 deleted file mode 100644 index 9f6861ad589..00000000000 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt +++ /dev/null @@ -1,1367 +0,0 @@ -/* - * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -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.irIfThen -import org.jetbrains.kotlin.backend.jvm.functionByName -import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.builtins.StandardNames -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.builders.declarations.buildFun -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.deepCopyWithVariables -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.* -import org.jetbrains.kotlin.ir.symbols.* -import org.jetbrains.kotlin.ir.symbols.impl.* -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.ir.visitors.IrElementTransformerVoid -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.platform.jvm.isJvm -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal -import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeProjectionImpl -import org.jetbrains.kotlin.types.Variance -import org.jetbrains.kotlin.types.replace -import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections -import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound -import org.jetbrains.kotlin.util.OperatorNameConventions -import org.jetbrains.kotlinx.serialization.compiler.backend.common.* -import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.* -import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext -import org.jetbrains.kotlinx.serialization.compiler.resolve.* -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 IrBuilderExtension { - val compilerContext: SerializationPluginContext - - private val throwMissedFieldExceptionFunc - get() = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_NAME)).singleOrNull() - - private val throwMissedFieldExceptionArrayFunc - get() = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME)).singleOrNull() - - private val enumSerializerFactoryFunc - get() = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME)).singleOrNull() - - private val markedEnumSerializerFactoryFunc - get() = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME)).singleOrNull() - - private inline fun IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? { - return declarations.singleOrNull { it.descriptor == descriptor } as? T - } - - fun useFieldMissingOptimization(): Boolean { - return throwMissedFieldExceptionFunc != null && throwMissedFieldExceptionArrayFunc != null - } - - fun addFunctionBody(function: F, bodyGen: IrBlockBodyBuilder.(F) -> Unit) { - val parentClass = function.parent - val startOffset = function.startOffset.takeIf { it >= 0 } ?: parentClass.startOffset - val endOffset = function.endOffset.takeIf { it >= 0 } ?: parentClass.endOffset - function.body = DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody( - startOffset, - endOffset - ) { bodyGen(function) } - } - - fun IrClass.contributeFunction(descriptor: FunctionDescriptor, ignoreWhenMissing: Boolean = false, bodyGen: IrBlockBodyBuilder.(IrFunction) -> Unit) { - val f: IrSimpleFunction = searchForDeclaration(descriptor) - ?: (if (ignoreWhenMissing) return else compilerContext.symbolTable.referenceSimpleFunction(descriptor).owner) - f.body = DeclarationIrBuilder(compilerContext, f.symbol, this.startOffset, this.endOffset).irBlockBody( - this.startOffset, - this.endOffset - ) { bodyGen(f) } - } - - fun IrClass.contributeConstructor( - descriptor: ClassConstructorDescriptor, - declareNew: Boolean = true, - overwriteValueParameters: Boolean = false, - bodyGen: IrBlockBodyBuilder.(IrConstructor) -> Unit - ) { - val c: IrConstructor = searchForDeclaration(descriptor) ?: compilerContext.symbolTable.referenceConstructor(descriptor).owner - c.body = DeclarationIrBuilder(compilerContext, c.symbol, this.startOffset, this.endOffset).irBlockBody( - this.startOffset, - this.endOffset - ) { bodyGen(c) } - } - - fun IrClass.createLambdaExpression( - type: IrType, - bodyGen: IrBlockBodyBuilder.() -> Unit - ): IrFunctionExpression { - val function = compilerContext.irFactory.buildFun { - this.startOffset = this@createLambdaExpression.startOffset - this.endOffset = this@createLambdaExpression.endOffset - this.returnType = type - name = Name.identifier("") - visibility = DescriptorVisibilities.LOCAL - origin = SERIALIZABLE_PLUGIN_ORIGIN - } - function.body = - DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen) - function.parent = this - - val f0Type = compilerContext.irBuiltIns.functionN(0) - val f0ParamSymbol = f0Type.typeParameters[0].symbol - val f0IrType = f0Type.defaultType.substitute(mapOf(f0ParamSymbol to type)) - - return IrFunctionExpressionImpl( - startOffset, - endOffset, - f0IrType, - function, - IrStatementOrigin.LAMBDA - ) - } - - fun createLazyProperty( - containingClass: IrClass, - targetIrType: IrType, - name: Name, - 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 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)) - - val propertyDescriptor = - KSerializerDescriptorResolver.createValPropertyDescriptor( - Name.identifier(name.asString() + "\$delegate"), - containingClass.descriptor, - lazyIrType.toKotlinType(), - createGetter = true - ) - - return generateSimplePropertyWithBackingField(propertyDescriptor, containingClass).apply { - val builder = DeclarationIrBuilder(compilerContext, containingClass.symbol, startOffset, endOffset) - val initializerBody = builder.run { - val enumElement = IrGetEnumValueImpl( - startOffset, - endOffset, - lazySafeModeClassDescriptor.defaultType, - publicationEntryDescriptor.symbol - ) - - val lambdaExpression = containingClass.createLambdaExpression(targetIrType, initializerBuilder) - - irExprBody( - irInvoke(null, lazyFunctionSymbol, listOf(targetIrType), listOf(enumElement, lambdaExpression), lazyIrType) - ) - } - backingField!!.initializer = initializerBody - } - } - - fun createCompanionValProperty( - companionClass: IrClass, - type: IrType, - name: Name, - initializerBuilder: IrBlockBodyBuilder.() -> Unit - ): IrProperty { - val targetKotlinType = type.toKotlinType() - val propertyDescriptor = - KSerializerDescriptorResolver.createValPropertyDescriptor(name, companionClass.descriptor, targetKotlinType) - - return generateSimplePropertyWithBackingField(propertyDescriptor, companionClass, name).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) - } - } - } - - fun IrClass.contributeAnonymousInitializer(bodyGen: IrBlockBodyBuilder.() -> Unit) { - val symbol = IrAnonymousInitializerSymbolImpl(descriptor) - factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZABLE_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 - ) - } - - fun IrBuilderWithScope.irInvoke( - dispatchReceiver: IrExpression? = null, - callee: IrFunctionSymbol, - vararg args: IrExpression, - typeHint: IrType? = null - ): IrMemberAccessExpression<*> { - assert(callee.isBound) { "Symbol $callee expected to be bound" } - val returnType = typeHint ?: callee.owner.returnType - val call = irCall(callee, type = returnType) - call.dispatchReceiver = dispatchReceiver - args.forEachIndexed(call::putValueArgument) - return call - } - - fun IrBuilderWithScope.irInvoke( - dispatchReceiver: IrExpression? = null, - callee: IrFunctionSymbol, - typeArguments: List, - valueArguments: List, - returnTypeHint: IrType? = null - ): IrMemberAccessExpression<*> = - irInvoke( - dispatchReceiver, - callee, - *valueArguments.toTypedArray(), - typeHint = returnTypeHint - ).also { call -> typeArguments.forEachIndexed(call::putTypeArgument) } - - fun IrBuilderWithScope.createArrayOfExpression( - arrayElementType: IrType, - arrayElements: List - ): IrExpression { - - val arrayType = compilerContext.irBuiltIns.arrayClass.typeWith(arrayElementType) - val arg0 = IrVarargImpl(startOffset, endOffset, arrayType, arrayElementType, arrayElements) - val typeArguments = listOf(arrayElementType) - - return irCall(compilerContext.irBuiltIns.arrayOf, arrayType, typeArguments = typeArguments).apply { - putValueArgument(0, arg0) - } - } - - fun IrBuilderWithScope.createPrimitiveArrayOfExpression( - elementPrimitiveType: IrType, - arrayElements: List - ): IrExpression { - val arrayType = compilerContext.irBuiltIns.primitiveArrayForType.getValue(elementPrimitiveType).defaultType - val arg0 = IrVarargImpl(startOffset, endOffset, arrayType, elementPrimitiveType, arrayElements) - val typeArguments = listOf(elementPrimitiveType) - - return irCall(compilerContext.irBuiltIns.arrayOf, arrayType, typeArguments = typeArguments).apply { - putValueArgument(0, arg0) - } - } - - 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() - return irInvoke(lhs, symbol, rhs) - } - - fun IrBuilderWithScope.irGetObject(irObject: IrClass) = - IrGetObjectValueImpl( - startOffset, - endOffset, - irObject.defaultType, - irObject.symbol - ) - - fun T.buildWithScope(builder: (T) -> Unit): T = - also { irDeclaration -> - compilerContext.symbolTable.withReferenceScope(irDeclaration) { - builder(irDeclaration) - } - } - - class BranchBuilder( - val irWhen: IrWhen, - context: IrGeneratorContext, - scope: Scope, - startOffset: Int, - endOffset: Int - ) : IrBuilderWithScope(context, scope, startOffset, endOffset) { - operator fun IrBranch.unaryPlus() { - irWhen.branches.add(this) - } - } - - fun IrBuilderWithScope.irWhen(typeHint: IrType? = null, block: BranchBuilder.() -> Unit): IrWhen { - val whenExpr = IrWhenImpl(startOffset, endOffset, typeHint ?: compilerContext.irBuiltIns.unitType) - val builder = BranchBuilder(whenExpr, context, scope, startOffset, endOffset) - builder.block() - return whenExpr - } - - fun BranchBuilder.elseBranch(result: IrExpression): IrElseBranch = - IrElseBranchImpl( - IrConstImpl.boolean(result.startOffset, result.endOffset, compilerContext.irBuiltIns.booleanType, true), - result - ) - - @FirIncompatiblePluginAPI - fun KotlinType.toIrType() = compilerContext.typeTranslator.translateType(this) - - // note: this method should be used only for properties from current module. Fields from other modules are private and inaccessible. - val IrSerializableProperty.irField: IrField? - get() = this.descriptor.backingField - - - - - /* - Create a function that creates `get property value expressions` for given corresponded constructor's param - (constructor_params) -> get_property_value_expression - */ - fun IrBuilderWithScope.createPropertyByParamReplacer( - irClass: IrClass, - serialProperties: List, - instance: IrValueParameter - ): (ValueParameterDescriptor) -> IrExpression? { - fun IrSerializableProperty.irGet(): IrExpression { - val ownerType = instance.symbol.owner.type - return getProperty( - irGet( - type = ownerType, - variable = instance.symbol - ), descriptor - ) - } - - val serialPropertiesMap = serialProperties.associateBy { it.descriptor } - - val transientPropertiesSet = - irClass.declarations.asSequence() - .filterIsInstance() - .filter { it.backingField != null } - .filter { !serialPropertiesMap.containsKey(it) } - .toSet() - - return { vpd -> - val propertyDescriptor = irClass.properties.find { it.name == vpd.name } - if (propertyDescriptor != null) { - val value = serialPropertiesMap[propertyDescriptor] - value?.irGet() ?: run { - if (propertyDescriptor in transientPropertiesSet) - getProperty( - irGet(instance), - propertyDescriptor - ) - else null - } - } else { - null - } - } - } - - fun IrBuilderWithScope.getProperty(receiver: IrExpression, property: IrProperty): IrExpression { - return if (property.getter != null) - irGet(property.getter!!.returnType, receiver, property.getter!!.symbol) - else - irGetField(receiver, property.backingField!!) - } - - fun IrBuilderWithScope.setProperty(receiver: IrExpression, property: IrProperty, value: IrExpression): IrExpression { - return if (property.setter != null) - irSet(property.setter!!.returnType, receiver, property.setter!!.symbol, value) - else - irSetField(receiver, property.backingField!!, value) - } - - /* - The rest of the file is mainly copied from FunctionGenerator. - However, I can't use it's directly because all generateSomething methods require KtProperty (psi element) - Also, FunctionGenerator itself has DeclarationGenerator as ctor param, which is a part of psi2ir - (it can be instantiated here, but I don't know how good is that idea) - */ - - fun IrBuilderWithScope.generateAnySuperConstructorCall(toBuilder: IrBlockBodyBuilder) { - val anyConstructor = compilerContext.irBuiltIns.anyClass.owner.declarations.single { it is IrConstructor } as IrConstructor - with(toBuilder) { - +IrDelegatingConstructorCallImpl.fromSymbolDescriptor( - startOffset, endOffset, - compilerContext.irBuiltIns.unitType, - anyConstructor.symbol - ) - } - } - - fun IrBlockBodyBuilder.generateGoldenMaskCheck( - seenVars: List, - properties: IrSerializableProperties, - serialDescriptor: IrExpression - ) { - val fieldsMissedTest: IrExpression - val throwErrorExpr: IrExpression - - val maskSlotCount = seenVars.size - if (maskSlotCount == 1) { - val goldenMask = properties.goldenMask - - - throwErrorExpr = irInvoke( - null, - throwMissedFieldExceptionFunc!!, - irGet(seenVars[0]), - irInt(goldenMask), - serialDescriptor, - typeHint = compilerContext.irBuiltIns.unitType - ) - - fieldsMissedTest = irNotEquals( - irInt(goldenMask), - irBinOp( - OperatorNameConventions.AND, - irInt(goldenMask), - irGet(seenVars[0]) - ) - ) - } else { - val goldenMaskList = properties.goldenMaskList - - var compositeExpression: IrExpression? = null - for (i in goldenMaskList.indices) { - val singleCheckExpr = irNotEquals( - irInt(goldenMaskList[i]), - irBinOp( - OperatorNameConventions.AND, - irInt(goldenMaskList[i]), - irGet(seenVars[i]) - ) - ) - - compositeExpression = if (compositeExpression == null) { - singleCheckExpr - } else { - irBinOp( - OperatorNameConventions.OR, - compositeExpression, - singleCheckExpr - ) - } - } - - fieldsMissedTest = compositeExpression!! - - throwErrorExpr = irBlock { - +irInvoke( - null, - throwMissedFieldExceptionArrayFunc!!, - createPrimitiveArrayOfExpression(compilerContext.irBuiltIns.intType, goldenMaskList.indices.map { irGet(seenVars[it]) }), - createPrimitiveArrayOfExpression(compilerContext.irBuiltIns.intType, goldenMaskList.map { irInt(it) }), - serialDescriptor, - typeHint = compilerContext.irBuiltIns.unitType - ) - } - } - - +irIfThen(compilerContext.irBuiltIns.unitType, fieldsMissedTest, throwErrorExpr) - } - - fun generateSimplePropertyWithBackingField( - propertyDescriptor: PropertyDescriptor, - propertyParent: IrClass, - fieldName: Name = propertyDescriptor.name, - ): IrProperty { - val irProperty = propertyParent.searchForDeclaration(propertyDescriptor) ?: run { - with(propertyDescriptor) { - propertyParent.factory.createProperty( - propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrPropertySymbolImpl(propertyDescriptor), - name, visibility, modality, isVar, isConst, isLateInit, isDelegated, isExternal - ).also { - it.parent = propertyParent - propertyParent.addMember(it) - } - } - } - - propertyParent.generatePropertyBackingFieldIfNeeded(propertyDescriptor, irProperty, fieldName) - val fieldSymbol = irProperty.backingField!!.symbol - irProperty.getter = propertyDescriptor.getter?.let { - propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = true) - }?.apply { parent = propertyParent } - irProperty.setter = propertyDescriptor.setter?.let { - propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = false) - }?.apply { parent = propertyParent } - return irProperty - } - - fun IrType.kClassToJClassIfNeeded(): IrType = this - - fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression = irExpression - - private fun IrClass.generatePropertyBackingFieldIfNeeded( - propertyDescriptor: PropertyDescriptor, - originProperty: IrProperty, - name: Name, - ) { - if (originProperty.backingField != null) return - - val field = with(propertyDescriptor) { - // TODO: type parameters - @OptIn(FirIncompatiblePluginAPI::class)// should be called only with old FE - originProperty.factory.createField( - originProperty.startOffset, originProperty.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrFieldSymbolImpl(propertyDescriptor), name, type.toIrType(), - visibility, !isVar, isEffectivelyExternal(), dispatchReceiverParameter == null - ) - } - field.apply { - parent = this@generatePropertyBackingFieldIfNeeded - correspondingPropertySymbol = originProperty.symbol - } - - originProperty.backingField = field - } - - private fun IrClass.generatePropertyAccessor( - propertyDescriptor: PropertyDescriptor, - property: IrProperty, - descriptor: PropertyAccessorDescriptor, - fieldSymbol: IrFieldSymbol, - isGetter: Boolean, - ): IrSimpleFunction { - val irAccessor: IrSimpleFunction = when (isGetter) { - true -> searchForDeclaration(propertyDescriptor)?.getter - false -> searchForDeclaration(propertyDescriptor)?.setter - } ?: run { - with(descriptor) { - @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend - property.factory.createFunction( - fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor), - name, visibility, modality, returnType!!.toIrType(), - isInline, isEffectivelyExternal(), isTailrec, isSuspend, isOperator, isInfix, isExpect - ) - }.also { f -> - generateOverriddenFunctionSymbols(f, compilerContext.symbolTable) - f.createParameterDeclarations(descriptor) - @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend - f.returnType = descriptor.returnType!!.toIrType() - f.correspondingPropertySymbol = fieldSymbol.owner.correspondingPropertySymbol - } - } - - irAccessor.body = when (isGetter) { - true -> generateDefaultGetterBody(descriptor as PropertyGetterDescriptor, irAccessor) - false -> generateDefaultSetterBody(descriptor as PropertySetterDescriptor, irAccessor) - } - - return irAccessor - } - - private fun generateDefaultGetterBody( - getter: PropertyGetterDescriptor, - irAccessor: IrSimpleFunction - ): IrBlockBody { - val property = getter.correspondingProperty - val irProperty = irAccessor.correspondingPropertySymbol?.owner ?: error("Expected property for $getter") - - val startOffset = irAccessor.startOffset - val endOffset = irAccessor.endOffset - val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset) - - val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol, property) - - val propertyIrType = irAccessor.returnType - irBody.statements.add( - IrReturnImpl( - startOffset, endOffset, compilerContext.irBuiltIns.nothingType, - irAccessor.symbol, - IrGetFieldImpl( - startOffset, endOffset, - irProperty.backingField?.symbol ?: error("Property expected to have backing field"), - propertyIrType, - receiver - ).let { - if (propertyIrType.isKClass()) { - irAccessor.returnType = irAccessor.returnType.kClassToJClassIfNeeded() - kClassExprToJClassIfNeeded(startOffset, endOffset, it) - } else it - } - ) - ) - return irBody - } - - private fun generateDefaultSetterBody( - setter: PropertySetterDescriptor, - irAccessor: IrSimpleFunction - ): IrBlockBody { - val property = setter.correspondingProperty - val irProperty = irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor $setter") - val startOffset = irAccessor.startOffset - val endOffset = irAccessor.endOffset - val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset) - - val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol, property) - - val irValueParameter = irAccessor.valueParameters.single() - irBody.statements.add( - IrSetFieldImpl( - startOffset, endOffset, - irProperty.backingField?.symbol ?: error("Property $property expected to have backing field"), - receiver, - IrGetValueImpl(startOffset, endOffset, irValueParameter.type, irValueParameter.symbol), - compilerContext.irBuiltIns.unitType - ) - ) - return irBody - } - - fun generateReceiverExpressionForFieldAccess( // todo: remove this - ownerSymbol: IrValueSymbol, - property: PropertyDescriptor - ): IrExpression { - val containingDeclaration = property.containingDeclaration - return when (containingDeclaration) { - is ClassDescriptor -> - IrGetValueImpl( - ownerSymbol.owner.startOffset, ownerSymbol.owner.endOffset, - ownerSymbol - ) - else -> throw AssertionError("Property must be in class") - } - } - - fun IrFunction.createParameterDeclarations( - descriptor: FunctionDescriptor, - overwriteValueParameters: Boolean = false, - copyTypeParameters: Boolean = true - ) { - val function = this - fun irValueParameter(descriptor: ParameterDescriptor): IrValueParameter = with(descriptor) { - @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend - factory.createValueParameter( - function.startOffset, function.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this), - name, indexOrMinusOne, type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline, - isHidden = false, isAssignable = false - ).also { - it.parent = function - } - } - - if (copyTypeParameters) { - assert(typeParameters.isEmpty()) - copyTypeParamsFromDescriptor(descriptor) - } - - dispatchReceiverParameter = descriptor.dispatchReceiverParameter?.let { irValueParameter(it) } - extensionReceiverParameter = descriptor.extensionReceiverParameter?.let { irValueParameter(it) } - - if (!overwriteValueParameters) - assert(valueParameters.isEmpty()) - - valueParameters = descriptor.valueParameters.map { irValueParameter(it) } - } - - fun IrFunction.copyTypeParamsFromDescriptor(descriptor: FunctionDescriptor) { - val newTypeParameters = descriptor.typeParameters.map { - factory.createTypeParameter( - startOffset, endOffset, - SERIALIZABLE_PLUGIN_ORIGIN, - IrTypeParameterSymbolImpl(it), - it.name, it.index, it.isReified, it.variance - ).also { typeParameter -> - typeParameter.parent = this - } - } - @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend - newTypeParameters.forEach { typeParameter -> - typeParameter.superTypes = typeParameter.descriptor.upperBounds.map { it.toIrType() } - } - - typeParameters = newTypeParameters - } - - fun createClassReference(classType: IrType, startOffset: Int, endOffset: Int): IrClassReference { - return IrClassReferenceImpl( - startOffset, - endOffset, - compilerContext.irBuiltIns.kClassClass.starProjectedType, - classType.classifierOrFail, - classType - ) - } - - // It is impossible to use star projections right away, because Array::class differ from Array::class : - // detailed information about type arguments required for class references on jvm - private val KotlinType.approximateJvmErasure: KotlinType - get() = when (val classifier = constructor.declarationDescriptor) { - is TypeParameterDescriptor -> { - // Note that if the upper bound is Array, the argument type will be replaced with star here, which is probably incorrect. - classifier.classBound.defaultType.replaceArgumentsWithStarProjections() - } - is ClassDescriptor -> if (KotlinBuiltIns.isArray(this)) { - replace(arguments.map { TypeProjectionImpl(Variance.INVARIANT, it.type.approximateJvmErasure) }) - } else { - replaceArgumentsWithStarProjections() - } - else -> error("Unsupported classifier: $this") - } - - private val TypeParameterDescriptor.classBound: ClassDescriptor - get() = when (val bound = representativeUpperBound.constructor.declarationDescriptor) { - is ClassDescriptor -> bound - is TypeParameterDescriptor -> bound.classBound - else -> error("Unsupported classifier: $this") - } - - - fun IrBuilderWithScope.classReference(classSymbol: IrType): IrClassReference = - createClassReference(classSymbol, startOffset, endOffset) - - private fun extractDefaultValuesFromConstructor(irClass: IrClass?): Map { - if (irClass == null) return emptyMap() - val original = irClass.constructors.singleOrNull { it.isPrimary } - // default arguments of original constructor - val defaultsMap: Map = - original?.valueParameters?.associate { it.symbol to it.defaultValue?.expression } ?: emptyMap() - return defaultsMap + extractDefaultValuesFromConstructor(irClass.getSuperClassNotAny()) - } - - /* - Creates an initializer adapter function that can replace IR expressions of getting constructor parameter value by some other expression. - Also adapter may replace IR expression of getting `this` value by another expression. - */ - fun createInitializerAdapter( - irClass: IrClass, - paramGetReplacer: (ValueParameterDescriptor) -> IrExpression?, - thisGetReplacer: Pair IrExpression>? = null - ): (IrExpressionBody) -> IrExpression { - val initializerTransformer = object : IrElementTransformerVoid() { - // try to replace `get some value` expression - override fun visitGetValue(expression: IrGetValue): IrExpression { - val symbol = expression.symbol - if (thisGetReplacer != null && thisGetReplacer.first == symbol) { - // replace `get this value` expression - return thisGetReplacer.second() - } - - val descriptor = symbol.descriptor - if (descriptor is ValueParameterDescriptor) { - // replace `get parameter value` expression - paramGetReplacer(descriptor)?.let { return it } - } - - // otherwise leave expression as it is - return super.visitGetValue(expression) - } - } - val defaultsMap = extractDefaultValuesFromConstructor(irClass) - return fun(initializer: IrExpressionBody): IrExpression { - val rawExpression = initializer.expression - val expression = - if (rawExpression.isInitializePropertyFromParameter()) { - // this is a primary constructor property, use corresponding default of value parameter - defaultsMap.getValue((rawExpression as IrGetValue).symbol)!! - } else { - rawExpression - } - return expression.deepCopyWithVariables().transform(initializerTransformer, null) - } - } - - private fun getEnumMembersNames(enumClass: ClassDescriptor): Sequence { - assert(enumClass.kind == ClassKind.ENUM_CLASS) - return enumClass.unsubstitutedMemberScope.getContributedDescriptors().asSequence() - .filterIsInstance() - .filter { it.kind == ClassKind.ENUM_ENTRY } - .map { it.name.toString() } - } - - fun IrBuilderWithScope.copyAnnotationsFrom(annotations: List): List = - annotations.mapNotNull { annotationCall -> - val annotationClass = annotationCall.symbol.owner.parentAsClass - if (!annotationClass.descriptor.isSerialInfoAnnotation) return@mapNotNull null - - if (compilerContext.platform.isJvm()) { - val implClass = compilerContext.serialInfoImplJvmIrGenerator.getImplClass(annotationClass) - val ctor = implClass.constructors.singleOrNull { it.valueParameters.size == annotationCall.valueArgumentsCount } - ?: error("No constructor args found for SerialInfo annotation Impl class: ${implClass.render()}") - irCall(ctor).apply { - for (i in 0 until annotationCall.valueArgumentsCount) { - val argument = annotationCall.getValueArgument(i) - ?: annotationClass.primaryConstructor!!.valueParameters[i].defaultValue?.expression - putValueArgument(i, argument!!.deepCopyWithVariables()) - } - } - } else { - annotationCall.deepCopyWithVariables() - } - } - - // Does not use sti and therefore does not perform encoder calls optimization - fun IrBuilderWithScope.serializerTower( - generator: SerializerIrGenerator, - dispatchReceiverParameter: IrValueParameter, - property: IrSerializableProperty - ): 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( - generator, - dispatchReceiverParameter, - serializer, - compilerContext, - property.type, - genericIndex = property.genericIndex - ) - ?.let { expr -> wrapWithNullableSerializerIfNeeded(property.type, expr, nullableSerClass) } - } - - private fun IrBuilderWithScope.wrapWithNullableSerializerIfNeeded( - type: IrType, - expression: IrExpression, - nullableProp: IrPropertySymbol - ): IrExpression = if (type.isMarkedNullable()) { - val resultType = type.makeNotNull() - val typeArguments = listOf(resultType) - val callee = nullableProp.owner.getter!! - - val returnType = callee.returnType.substitute(callee.typeParameters, typeArguments) - - irInvoke( - callee = callee.symbol, - typeArguments = typeArguments, - valueArguments = emptyList(), - returnTypeHint = returnType - ).apply { extensionReceiver = expression } - } else { - expression - } - - fun wrapIrTypeIntoKSerializerIrType( - type: IrType, - variance: Variance = Variance.INVARIANT - ): IrType { - val kSerClass = compilerContext.referenceClass(ClassId(SerializationPackages.packageFqName, SerialEntityNames.KSERIALIZER_NAME)) - ?: error("Couldn't find class ${SerialEntityNames.KSERIALIZER_NAME}") - return IrSimpleTypeImpl( - kSerClass, hasQuestionMark = false, arguments = listOf( - makeTypeProjection(type, variance) - ), annotations = emptyList() - ) - } - - fun IrBuilderWithScope.serializerInstance( - enclosingGenerator: SerializerIrGenerator, - dispatchReceiverParameter: IrValueParameter, - serializerClassOriginal: IrClassSymbol?, - pluginContext: SerializationPluginContext, - kType: IrType, - genericIndex: Int? = null - ): IrExpression? = serializerInstance( - enclosingGenerator, - serializerClassOriginal, - pluginContext, - kType, - genericIndex - ) { it, _ -> - val (_, ir) = enclosingGenerator.localSerializersFieldsDescriptors[it] - irGetField(irGet(dispatchReceiverParameter), ir.backingField!!) - } - - fun IrBuilderWithScope.serializerInstance( - enclosingGenerator: AbstractIrGenerator, - serializerClassOriginal: ClassDescriptor?, - module: ModuleDescriptor, - kType: IrType, - genericIndex: Int? = null, - genericGetter: ((Int) -> IrExpression)? = null - ): IrExpression? { - return serializerInstance( - enclosingGenerator, - serializerClassOriginal?.let { compilerContext.symbolTable.referenceClass(it) }, - compilerContext, - kType, - genericIndex, - if (genericGetter != null) { i, _ -> genericGetter(i) } else null - ) - } - - fun IrBuilderWithScope.serializerInstance( - enclosingGenerator: AbstractIrGenerator, - serializerClassOriginal: IrClassSymbol?, - pluginContext: SerializationPluginContext, - kType: IrType, - genericIndex: Int? = null, - genericGetter: ((Int, IrType) -> IrExpression)? = null - ): IrExpression? { - val nullableSerClass = compilerContext.referenceProperties(SerialEntityNames.wrapIntoNullableCallableId).single() - if (serializerClassOriginal == null) { - if (genericIndex == null) return null - return genericGetter?.invoke(genericIndex, kType) - } - if (serializerClassOriginal.owner.kind == ClassKind.OBJECT) { - return irGetObject(serializerClassOriginal) - } - fun instantiate(serializer: IrClassSymbol?, type: IrType): IrExpression? { - val expr = serializerInstance( - enclosingGenerator, - serializer, - pluginContext, - type, - type.genericIndex, - genericGetter - ) ?: return null - return wrapWithNullableSerializerIfNeeded(type, expr, nullableSerClass) - } - - var serializerClass = serializerClassOriginal - var args: List - var typeArgs: List - val thisIrType = (kType as? IrSimpleType) ?: error("Don't know how to work with type ${kType::class}") - var needToCopyAnnotations = false - - when (serializerClassOriginal.owner.classId) { - polymorphicSerializerId -> { - needToCopyAnnotations = true - args = listOf(classReference(kType)) - typeArgs = listOf(thisIrType) - } - contextSerializerId -> { - args = listOf(classReference(kType)) - typeArgs = listOf(thisIrType) - - val hasNewCtxSerCtor = compilerContext.referenceConstructors(contextSerializerId).any { it.owner.valueParameters.size == 3 } - - if (hasNewCtxSerCtor) { - // new signature of context serializer - args = args + mutableListOf().apply { - val fallbackDefaultSerializer = findTypeSerializer(pluginContext, kType) - add(instantiate(fallbackDefaultSerializer, kType) ?: irNull()) - add( - createArrayOfExpression( - wrapIrTypeIntoKSerializerIrType( - thisIrType, - variance = Variance.OUT_VARIANCE - ), - thisIrType.arguments.map { - val argSer = enclosingGenerator.findTypeSerializerOrContext( - compilerContext, - it.typeOrNull!! //todo: handle star projections here? - ) - instantiate(argSer, it.typeOrNull!!)!! - }) - ) - } - } - } - objectSerializerId -> { - needToCopyAnnotations = true - args = listOf(irString(kType.serialName()), irGetObject(kType.classOrNull!!)) - typeArgs = listOf(thisIrType) - } - sealedSerializerId -> { - needToCopyAnnotations = true - args = mutableListOf().apply { - add(irString(kType.serialName())) - add(classReference(kType)) - val (subclasses, subSerializers) = enclosingGenerator.allSealedSerializableSubclassesFor( - kType.classOrNull!!.owner, - pluginContext - ) - val projectedOutCurrentKClass = - compilerContext.irBuiltIns.kClassClass.typeWithArguments( - listOf(makeTypeProjection(thisIrType, Variance.OUT_VARIANCE)) - ) - add( - createArrayOfExpression( - projectedOutCurrentKClass, - subclasses.map { classReference(it) } - ) - ) - add( - createArrayOfExpression( - wrapIrTypeIntoKSerializerIrType(thisIrType, variance = Variance.OUT_VARIANCE), - subSerializers.mapIndexed { i, serializer -> - val type = subclasses[i] - val expr = serializerInstance( - enclosingGenerator, - serializer, - pluginContext, - type, - type.genericIndex - ) { _, genericType -> - serializerInstance( - enclosingGenerator, - pluginContext.referenceClass(polymorphicSerializerId), - pluginContext, - (genericType.classifierOrNull as IrTypeParameterSymbol).owner.representativeUpperBound - )!! - }!! - wrapWithNullableSerializerIfNeeded(type, expr, nullableSerClass) - } - ) - ) - } - typeArgs = listOf(thisIrType) - } - enumSerializerId -> { - serializerClass = pluginContext.referenceClass(enumSerializerId) - val enumDescriptor = kType.classOrNull!! - typeArgs = listOf(thisIrType) - // instantiate serializer only inside enum Companion - if (enclosingGenerator !is SerializableCompanionIrGenerator) { - // otherwise call Companion.serializer() - callSerializerFromCompanion(thisIrType, typeArgs, emptyList())?.let { return it } - } - - val enumArgs = mutableListOf( - irString(thisIrType.serialName()), - irCall(enumDescriptor.owner.findEnumValuesMethod()), - ) - - val enumSerializerFactoryFunc = enumSerializerFactoryFunc - val markedEnumSerializerFactoryFunc = markedEnumSerializerFactoryFunc - if (enumSerializerFactoryFunc != null && markedEnumSerializerFactoryFunc != null) { - // runtime contains enum serializer factory functions - val factoryFunc: IrSimpleFunctionSymbol = if (enumDescriptor.owner.isEnumWithSerialInfoAnnotation()) { - // need to store SerialInfo annotation in descriptor - val enumEntries = enumDescriptor.owner.enumEntries() - val entriesNames = enumEntries.map { it.annotations.serialNameValue?.let { n -> irString(n) } ?: irNull() } - val entriesAnnotations = enumEntries.map { - val annotationConstructors = it.annotations.map { a -> -// compilerContext.typeTranslator.constantValueGenerator.generateAnnotationConstructorCall(a) - a.deepCopyWithVariables() - } - val annotationsConstructors = copyAnnotationsFrom(annotationConstructors) - if (annotationsConstructors.isEmpty()) { - irNull() - } else { - createArrayOfExpression(compilerContext.irBuiltIns.annotationType, annotationsConstructors) - } - } - val annotationArrayType = - compilerContext.irBuiltIns.arrayClass.typeWith(compilerContext.irBuiltIns.annotationType.makeNullable()) - - enumArgs += createArrayOfExpression(compilerContext.irBuiltIns.stringType.makeNullable(), entriesNames) - enumArgs += createArrayOfExpression(annotationArrayType, entriesAnnotations) - - markedEnumSerializerFactoryFunc - } else { - enumSerializerFactoryFunc - } - - val factoryReturnType = factoryFunc.owner.returnType.substitute(factoryFunc.owner.typeParameters, typeArgs) - return irInvoke(null, factoryFunc, typeArgs, enumArgs, factoryReturnType) - } else { - // support legacy serializer instantiation by constructor for old runtimes - args = enumArgs - } - } - else -> { - args = kType.arguments.map { - val argSer = enclosingGenerator.findTypeSerializerOrContext( - pluginContext, - it.typeOrNull!! // todo: stars? - ) - instantiate(argSer, it.typeOrNull!!) ?: return null - } - typeArgs = kType.arguments.map { it.typeOrNull!! } - } - - } - if (serializerClassOriginal.owner.classId == referenceArraySerializerId) { - args = listOf(wrapperClassReference(kType.arguments.single().typeOrNull!!)) + args - typeArgs = listOf(typeArgs[0].makeNotNull()) + typeArgs - } - - // If KType is interface, .classSerializer always yields PolymorphicSerializer, which may be unavailable for interfaces from other modules - if (!kType.isInterface() && serializerClassOriginal == kType.classOrNull!!.owner.classSerializer(pluginContext) && enclosingGenerator !is SerializableCompanionIrGenerator) { - // This is default type serializer, we can shortcut through Companion.serializer() - // BUT not during generation of this method itself - callSerializerFromCompanion(thisIrType, typeArgs, args)?.let { return it } - } - - - val serializable = serializerClass?.owner?.let { getSerializableClassDescriptorBySerializer(it) } - requireNotNull(serializerClass) - val ctor = if (serializable?.typeParameters?.isNotEmpty() == true) { - requireNotNull( - findSerializerConstructorForTypeArgumentsSerializers(serializerClass.owner) - ) { "Generated serializer does not have constructor with required number of arguments" } - } else { - val constructors = serializerClass.constructors - // search for new signature of polymorphic/sealed/contextual serializer - if (!needToCopyAnnotations) { - constructors.single { it.owner.isPrimary } - } else { - constructors.find { it.owner.lastArgumentIsAnnotationArray() } ?: run { - // not found - we are using old serialization runtime without this feature - // todo: optimize allocating an empty array when no annotations defined, maybe use old constructor? - needToCopyAnnotations = false - constructors.single { it.owner.isPrimary } - } - } - } - // Return type should be correctly substituted - assert(ctor.isBound) - val ctorDecl = ctor.owner - if (needToCopyAnnotations) { - val classAnnotations = copyAnnotationsFrom(thisIrType.getClass()?.let { collectSerialInfoAnnotations(it) }.orEmpty()) - args = args + createArrayOfExpression(compilerContext.irBuiltIns.annotationType, classAnnotations) - } - - val typeParameters = ctorDecl.parentAsClass.typeParameters - val substitutedReturnType = ctorDecl.returnType.substitute(typeParameters, typeArgs) - return irInvoke( - null, - ctor, - // User may declare serializer with fixed type arguments, e.g. class SomeSerializer : KSerializer> - typeArguments = typeArgs.takeIf { it.size == ctorDecl.typeParameters.size }.orEmpty(), - valueArguments = args.takeIf { it.size == ctorDecl.valueParameters.size }.orEmpty(), - returnTypeHint = substitutedReturnType - ) - } - - fun collectSerialInfoAnnotations(irClass: IrClass): List { - if (!(irClass.isInterface || irClass.descriptor.hasSerializableOrMetaAnnotation)) return emptyList() - val annotationByFq: MutableMap = - irClass.annotations.associateBy { it.symbol.owner.parentAsClass.descriptor.fqNameSafe }.toMutableMap() - for (clazz in irClass.getAllSuperclasses()) { - val annotations = clazz.annotations - .mapNotNull { - val descriptor = it.symbol.owner.parentAsClass.descriptor - if (descriptor.isInheritableSerialInfoAnnotation) descriptor.fqNameSafe to it else null - } - annotations.forEach { (fqname, call) -> - if (fqname !in annotationByFq) { - annotationByFq[fqname] = call - } else { - // SerializationPluginDeclarationChecker already reported inconsistency - } - } - } - return annotationByFq.values.toList() - } - - fun IrBuilderWithScope.callSerializerFromCompanion( - thisIrType: IrType, - typeArgs: List, - args: List - ): IrExpression? { - val baseClass = thisIrType.getClass() ?: return null - val companionClass = baseClass.companionObject() ?: return null - val serializerProviderFunction = companionClass.declarations.singleOrNull { - it is IrFunction && it.name == SerialEntityNames.SERIALIZER_PROVIDER_NAME && it.valueParameters.size == baseClass.typeParameters.size - } ?: return null - - val adjustedArgs: List = - // if typeArgs.size == args.size then the serializer is custom - we need to use the actual serializers from the arguments - if ((typeArgs.size != args.size) && (baseClass.descriptor.isSealed() || baseClass.descriptor.modality == Modality.ABSTRACT)) { - val serializer = findStandardKotlinTypeSerializer(compilerContext, context.irBuiltIns.unitType)!! - // workaround for sealed and abstract classes - the `serializer` function expects non-null serializers, but does not use them, so serializers of any type can be passed - List(baseClass.typeParameters.size) { irGetObject(serializer) } - } else { - args - } - - with(serializerProviderFunction as IrFunction) { - // Note that [typeArgs] may be unused if we short-cut to e.g. SealedClassSerializer - return irInvoke( - irGetObject(companionClass), - symbol, - typeArgs.takeIf { it.size == typeParameters.size }.orEmpty(), - adjustedArgs.takeIf { it.size == valueParameters.size }.orEmpty() - ) - } - } - - private fun IrConstructor.lastArgumentIsAnnotationArray(): Boolean { - val lastArgType = valueParameters.lastOrNull()?.type - if (lastArgType == null || !lastArgType.isArray()) return false - return ((lastArgType as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull?.classFqName?.toString() == "kotlin.Annotation") - } - - private fun IrBuilderWithScope.wrapperClassReference(classType: IrType): IrClassReference { - if (compilerContext.platform.isJvm()) { - // "Byte::class" -> "java.lang.Byte::class" -// TODO: get rid of descriptor - val wrapperFqName = KotlinBuiltIns.getPrimitiveType(classType.classOrNull!!.descriptor)?.let(JvmPrimitiveType::get)?.wrapperFqName - if (wrapperFqName != null) { - val wrapperClass = compilerContext.referenceClass(ClassId.topLevel(wrapperFqName)) - ?: error("Primitive wrapper class for $classType not found: $wrapperFqName") - return classReference(wrapperClass.defaultType) - } - } - return classReference(classType) - } - - fun findSerializerConstructorForTypeArgumentsSerializers(serializer: IrClass): IrConstructorSymbol? { - - val typeParamsCount = ((serializer.superTypes.find { isKSerializer(it) } as IrSimpleType).arguments.first().typeOrNull!! as IrSimpleType).arguments.size - if (typeParamsCount == 0) return null //don't need it - - return serializer.constructors.singleOrNull { - it.valueParameters.let { vps -> vps.size == typeParamsCount && vps.all { vp -> isKSerializer(vp.type) } } - }?.symbol - } - - private fun IrConstructor.isSerializationCtor(): Boolean { - val serialMarker = - compilerContext.referenceClass(ClassId.topLevel(SerializationPackages.internalPackageFqName.child(SerialEntityNames.SERIAL_CTOR_MARKER_NAME))) - - return valueParameters.lastOrNull()?.run { - name == SerialEntityNames.dummyParamName && type.classifierOrNull == serialMarker - } == true - } - - fun serializableSyntheticConstructor(forClass: IrClass): IrConstructorSymbol? { // todo: remove this W/A when FIR plugin will add proper ctor - return forClass.declarations.filterIsInstance().singleOrNull { it.isSerializationCtor() }?.symbol - } - - fun IrClass.getSuperClassOrAny(): IrClass = getSuperClassNotAny() ?: compilerContext.irBuiltIns.anyClass.owner - - fun IrClass.getSuperClassNotAny(): IrClass? { - val superClasses = superTypes.mapNotNull { it.classOrNull }.map { it.owner } - - return superClasses.singleOrNull { it.kind == ClassKind.CLASS } - } - - fun IrClass.findWriteSelfMethod(): IrSimpleFunction? = - declarations.singleOrNull { it is IrSimpleFunction && it.name == SerialEntityNames.WRITE_SELF_NAME && !it.isFakeOverride } as IrSimpleFunction? - - fun IrBlockBodyBuilder.serializeAllProperties( - generator: AbstractIrGenerator, - serializableIrClass: IrClass, - serializableProperties: List, - objectToSerialize: IrValueDeclaration, - localOutput: IrValueDeclaration, - localSerialDesc: IrValueDeclaration, - kOutputClass: IrClassSymbol, - ignoreIndexTo: Int, - initializerAdapter: (IrExpressionBody) -> IrExpression, - genericGetter: ((Int, IrType) -> IrExpression)? - ) { - - fun IrSerializableProperty.irGet(): IrExpression { - val ownerType = objectToSerialize.symbol.owner.type - return getProperty( - irGet( - type = ownerType, - variable = objectToSerialize.symbol - ), descriptor - ) - } - - for ((index, property) in serializableProperties.withIndex()) { - if (index < ignoreIndexTo) continue - // output.writeXxxElementValue(classDesc, index, value) - val elementCall = formEncodeDecodePropertyCall( - generator, - irGet(localOutput), - property, { innerSerial, sti -> - val f = - kOutputClass.functionByName("${CallingConventions.encode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}") - f to listOf( - irGet(localSerialDesc), - irInt(index), - innerSerial, - property.irGet() - ) - }, { - val f = - kOutputClass.functionByName("${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 - }, - genericGetter - ) - - // check for call to .shouldEncodeElementDefault - val encodeDefaults = property.descriptor.getEncodeDefaultAnnotationValue() - val field = property.irField // Nullable when property from another module; can't compare it with default value on JS or Native - if (!property.optional || encodeDefaults == true || field == null) { - // emit call right away - +elementCall - } else { - val partB = irNotEquals(property.irGet(), initializerAdapter(field.initializer!!)) - - val condition = if (encodeDefaults == false) { - // drop default without call to .shouldEncodeElementDefault - partB - } else { - // emit check: - // 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.functionByName(CallingConventions.shouldEncodeDefault) - val partA = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index)) - // Ir infrastructure does not have dedicated symbol for ||, so - // `a || b == if (a) true else b`, see org.jetbrains.kotlin.ir.builders.PrimitivesKt.oror - irIfThenElse(compilerContext.irBuiltIns.booleanType, partA, irTrue(), partB) - } - +irIfThen(condition, elementCall) - } - } - } - - /** - * True — ALWAYS - * False — NEVER - * null — not specified - */ - fun IrProperty.getEncodeDefaultAnnotationValue(): Boolean? { - val call = annotations.findAnnotation(SerializationAnnotations.encodeDefaultFqName) ?: return null - val arg = call.getValueArgument(0) ?: return true // ALWAYS by default - val argValue = (arg as? IrGetEnumValue - ?: error("Argument of enum constructor expected to implement IrGetEnumValue, got $arg")).symbol.owner.name.toString() - return when (argValue) { - "ALWAYS" -> true - "NEVER" -> false - else -> error("Unknown EncodeDefaultMode enum value: $argValue") - } - } - - - fun IrBlockBodyBuilder.formEncodeDecodePropertyCall( - enclosingGenerator: AbstractIrGenerator, - encoder: IrExpression, - property: IrSerializableProperty, - whenHaveSerializer: (serializer: IrExpression, sti: IrSerialTypeInfo) -> FunctionWithArgs, - whenDoNot: (sti: IrSerialTypeInfo) -> FunctionWithArgs, - genericGetter: ((Int, IrType) -> IrExpression)? = null, - returnTypeHint: IrType? = null - ): IrExpression { - val sti = enclosingGenerator.getIrSerialTypeInfo(property, compilerContext) - val innerSerial = serializerInstance( - enclosingGenerator, - sti.serializer, - compilerContext, - property.type, - property.genericIndex, - genericGetter - ) - val (functionToCall, args: List) = if (innerSerial != null) whenHaveSerializer(innerSerial, sti) else whenDoNot(sti) - val typeArgs = if (functionToCall.descriptor.typeParameters.isNotEmpty()) listOf(property.type) else listOf() - return irInvoke(encoder, functionToCall, typeArguments = typeArgs, valueArguments = args, returnTypeHint = returnTypeHint) - } - -} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt new file mode 100644 index 00000000000..9a658ccc21e --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt @@ -0,0 +1,569 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +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.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.builders.declarations.buildFun +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.deepCopyWithVariables +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrValueSymbol +import org.jetbrains.kotlin.ir.symbols.impl.* +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.* +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.platform.jvm.isJvm +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal +import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_FQ +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_MODE_FQ +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_PUBLICATION_MODE_NAME +import org.jetbrains.kotlinx.serialization.compiler.resolve.hasSerializableOrMetaAnnotation +import org.jetbrains.kotlinx.serialization.compiler.resolve.isInheritableSerialInfoAnnotation +import org.jetbrains.kotlinx.serialization.compiler.resolve.isSerialInfoAnnotation + + +interface IrBuilderWithPluginContext { + val compilerContext: SerializationPluginContext + + fun addFunctionBody(function: F, bodyGen: IrBlockBodyBuilder.(F) -> Unit) { + val parentClass = function.parent + val startOffset = function.startOffset.takeIf { it >= 0 } ?: parentClass.startOffset + val endOffset = function.endOffset.takeIf { it >= 0 } ?: parentClass.endOffset + function.body = DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody( + startOffset, + endOffset + ) { bodyGen(function) } + } + + fun IrClass.createLambdaExpression( + type: IrType, + bodyGen: IrBlockBodyBuilder.() -> Unit + ): IrFunctionExpression { + val function = compilerContext.irFactory.buildFun { + this.startOffset = this@createLambdaExpression.startOffset + this.endOffset = this@createLambdaExpression.endOffset + this.returnType = type + name = Name.identifier("") + visibility = DescriptorVisibilities.LOCAL + origin = SERIALIZABLE_PLUGIN_ORIGIN + } + function.body = + DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen) + function.parent = this + + val f0Type = compilerContext.irBuiltIns.functionN(0) + val f0ParamSymbol = f0Type.typeParameters[0].symbol + val f0IrType = f0Type.defaultType.substitute(mapOf(f0ParamSymbol to type)) + + return IrFunctionExpressionImpl( + startOffset, + endOffset, + f0IrType, + function, + IrStatementOrigin.LAMBDA + ) + } + + fun createLazyProperty( + containingClass: IrClass, + targetIrType: IrType, + name: Name, + 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 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)) + + val propertyDescriptor = + KSerializerDescriptorResolver.createValPropertyDescriptor( + Name.identifier(name.asString() + "\$delegate"), + containingClass.descriptor, + lazyIrType.toKotlinType(), + createGetter = true + ) + + return generateSimplePropertyWithBackingField(propertyDescriptor, containingClass).apply { + val builder = DeclarationIrBuilder(compilerContext, containingClass.symbol, startOffset, endOffset) + val initializerBody = builder.run { + val enumElement = IrGetEnumValueImpl( + startOffset, + endOffset, + lazySafeModeClassDescriptor.defaultType, + publicationEntryDescriptor.symbol + ) + + val lambdaExpression = containingClass.createLambdaExpression(targetIrType, initializerBuilder) + + irExprBody( + irInvoke(null, lazyFunctionSymbol, listOf(targetIrType), listOf(enumElement, lambdaExpression), lazyIrType) + ) + } + backingField!!.initializer = initializerBody + } + } + + fun createCompanionValProperty( + companionClass: IrClass, + type: IrType, + name: Name, + initializerBuilder: IrBlockBodyBuilder.() -> Unit + ): IrProperty { + val targetKotlinType = type.toKotlinType() + val propertyDescriptor = + KSerializerDescriptorResolver.createValPropertyDescriptor(name, companionClass.descriptor, targetKotlinType) + + return generateSimplePropertyWithBackingField(propertyDescriptor, companionClass, name).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) + } + } + } + + fun IrClass.contributeAnonymousInitializer(bodyGen: IrBlockBodyBuilder.() -> Unit) { + val symbol = IrAnonymousInitializerSymbolImpl(descriptor) + factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZABLE_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 + ) + } + + fun IrBuilderWithScope.irInvoke( + dispatchReceiver: IrExpression? = null, + callee: IrFunctionSymbol, + vararg args: IrExpression, + typeHint: IrType? = null + ): IrMemberAccessExpression<*> { + assert(callee.isBound) { "Symbol $callee expected to be bound" } + val returnType = typeHint ?: callee.owner.returnType + val call = irCall(callee, type = returnType) + call.dispatchReceiver = dispatchReceiver + args.forEachIndexed(call::putValueArgument) + return call + } + + fun IrBuilderWithScope.irInvoke( + dispatchReceiver: IrExpression? = null, + callee: IrFunctionSymbol, + typeArguments: List, + valueArguments: List, + returnTypeHint: IrType? = null + ): IrMemberAccessExpression<*> = + irInvoke( + dispatchReceiver, + callee, + *valueArguments.toTypedArray(), + typeHint = returnTypeHint + ).also { call -> typeArguments.forEachIndexed(call::putTypeArgument) } + + fun IrBuilderWithScope.createArrayOfExpression( + arrayElementType: IrType, + arrayElements: List + ): IrExpression { + + val arrayType = compilerContext.irBuiltIns.arrayClass.typeWith(arrayElementType) + val arg0 = IrVarargImpl(startOffset, endOffset, arrayType, arrayElementType, arrayElements) + val typeArguments = listOf(arrayElementType) + + return irCall(compilerContext.irBuiltIns.arrayOf, arrayType, typeArguments = typeArguments).apply { + putValueArgument(0, arg0) + } + } + + fun IrBuilderWithScope.createPrimitiveArrayOfExpression( + elementPrimitiveType: IrType, + arrayElements: List + ): IrExpression { + val arrayType = compilerContext.irBuiltIns.primitiveArrayForType.getValue(elementPrimitiveType).defaultType + val arg0 = IrVarargImpl(startOffset, endOffset, arrayType, elementPrimitiveType, arrayElements) + val typeArguments = listOf(elementPrimitiveType) + + return irCall(compilerContext.irBuiltIns.arrayOf, arrayType, typeArguments = typeArguments).apply { + putValueArgument(0, arg0) + } + } + + 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() + return irInvoke(lhs, symbol, rhs) + } + + fun IrBuilderWithScope.irGetObject(irObject: IrClass) = + IrGetObjectValueImpl( + startOffset, + endOffset, + irObject.defaultType, + irObject.symbol + ) + + fun T.buildWithScope(builder: (T) -> Unit): T = + also { irDeclaration -> + compilerContext.symbolTable.withReferenceScope(irDeclaration) { + builder(irDeclaration) + } + } + + class BranchBuilder( + val irWhen: IrWhen, + context: IrGeneratorContext, + scope: Scope, + startOffset: Int, + endOffset: Int + ) : IrBuilderWithScope(context, scope, startOffset, endOffset) { + operator fun IrBranch.unaryPlus() { + irWhen.branches.add(this) + } + } + + fun IrBuilderWithScope.irWhen(typeHint: IrType? = null, block: BranchBuilder.() -> Unit): IrWhen { + val whenExpr = IrWhenImpl(startOffset, endOffset, typeHint ?: compilerContext.irBuiltIns.unitType) + val builder = BranchBuilder(whenExpr, context, scope, startOffset, endOffset) + builder.block() + return whenExpr + } + + fun BranchBuilder.elseBranch(result: IrExpression): IrElseBranch = + IrElseBranchImpl( + IrConstImpl.boolean(result.startOffset, result.endOffset, compilerContext.irBuiltIns.booleanType, true), + result + ) + + @FirIncompatiblePluginAPI + fun KotlinType.toIrType() = compilerContext.typeTranslator.translateType(this) + + fun IrBuilderWithScope.setProperty(receiver: IrExpression, property: IrProperty, value: IrExpression): IrExpression { + return if (property.setter != null) + irSet(property.setter!!.returnType, receiver, property.setter!!.symbol, value) + else + irSetField(receiver, property.backingField!!, value) + } + + fun IrBuilderWithScope.generateAnySuperConstructorCall(toBuilder: IrBlockBodyBuilder) { + val anyConstructor = compilerContext.irBuiltIns.anyClass.owner.declarations.single { it is IrConstructor } as IrConstructor + with(toBuilder) { + +IrDelegatingConstructorCallImpl.fromSymbolDescriptor( + startOffset, endOffset, + compilerContext.irBuiltIns.unitType, + anyConstructor.symbol + ) + } + } + + private inline fun IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? { + return declarations.singleOrNull { it.descriptor == descriptor } as? T + } + + fun generateSimplePropertyWithBackingField( + propertyDescriptor: PropertyDescriptor, + propertyParent: IrClass, + fieldName: Name = propertyDescriptor.name, + ): IrProperty { + val irProperty = propertyParent.searchForDeclaration(propertyDescriptor) ?: run { + with(propertyDescriptor) { + propertyParent.factory.createProperty( + propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrPropertySymbolImpl(propertyDescriptor), + name, visibility, modality, isVar, isConst, isLateInit, isDelegated, isExternal + ).also { + it.parent = propertyParent + propertyParent.addMember(it) + } + } + } + + propertyParent.generatePropertyBackingFieldIfNeeded(propertyDescriptor, irProperty, fieldName) + val fieldSymbol = irProperty.backingField!!.symbol + irProperty.getter = propertyDescriptor.getter?.let { + propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = true) + }?.apply { parent = propertyParent } + irProperty.setter = propertyDescriptor.setter?.let { + propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = false) + }?.apply { parent = propertyParent } + return irProperty + } + + fun IrType.kClassToJClassIfNeeded(): IrType = this + + fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression = irExpression + + private fun IrClass.generatePropertyBackingFieldIfNeeded( + propertyDescriptor: PropertyDescriptor, + originProperty: IrProperty, + name: Name, + ) { + if (originProperty.backingField != null) return + + val field = with(propertyDescriptor) { + @OptIn(FirIncompatiblePluginAPI::class)// should be called only with old FE + originProperty.factory.createField( + originProperty.startOffset, originProperty.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrFieldSymbolImpl(propertyDescriptor), name, type.toIrType(), + visibility, !isVar, isEffectivelyExternal(), dispatchReceiverParameter == null + ) + } + field.apply { + parent = this@generatePropertyBackingFieldIfNeeded + correspondingPropertySymbol = originProperty.symbol + } + + originProperty.backingField = field + } + + private fun IrClass.generatePropertyAccessor( + propertyDescriptor: PropertyDescriptor, + property: IrProperty, + descriptor: PropertyAccessorDescriptor, + fieldSymbol: IrFieldSymbol, + isGetter: Boolean, + ): IrSimpleFunction { + val irAccessor: IrSimpleFunction = when (isGetter) { + true -> searchForDeclaration(propertyDescriptor)?.getter + false -> searchForDeclaration(propertyDescriptor)?.setter + } ?: run { + with(descriptor) { + @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend + property.factory.createFunction( + fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor), + name, visibility, modality, returnType!!.toIrType(), + isInline, isEffectivelyExternal(), isTailrec, isSuspend, isOperator, isInfix, isExpect + ) + }.also { f -> + generateOverriddenFunctionSymbols(f, compilerContext.symbolTable) + f.createParameterDeclarations(descriptor) + @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend + f.returnType = descriptor.returnType!!.toIrType() + f.correspondingPropertySymbol = fieldSymbol.owner.correspondingPropertySymbol + } + } + + irAccessor.body = when (isGetter) { + true -> generateDefaultGetterBody(irAccessor) + false -> generateDefaultSetterBody(irAccessor) + } + + return irAccessor + } + + private fun generateDefaultGetterBody( + irAccessor: IrSimpleFunction + ): IrBlockBody { + val irProperty = irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}") + + val startOffset = irAccessor.startOffset + val endOffset = irAccessor.endOffset + val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset) + + val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol) + + val propertyIrType = irAccessor.returnType + irBody.statements.add( + IrReturnImpl( + startOffset, endOffset, compilerContext.irBuiltIns.nothingType, + irAccessor.symbol, + IrGetFieldImpl( + startOffset, endOffset, + irProperty.backingField?.symbol ?: error("Property expected to have backing field"), + propertyIrType, + receiver + ).let { + if (propertyIrType.isKClass()) { + irAccessor.returnType = irAccessor.returnType.kClassToJClassIfNeeded() + kClassExprToJClassIfNeeded(startOffset, endOffset, it) + } else it + } + ) + ) + return irBody + } + + private fun generateDefaultSetterBody( + irAccessor: IrSimpleFunction + ): IrBlockBody { + val irProperty = irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}") + val startOffset = irAccessor.startOffset + val endOffset = irAccessor.endOffset + val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset) + + val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol) + + val irValueParameter = irAccessor.valueParameters.single() + irBody.statements.add( + IrSetFieldImpl( + startOffset, endOffset, + irProperty.backingField?.symbol ?: error("Property ${irProperty.render()} expected to have backing field"), + receiver, + IrGetValueImpl(startOffset, endOffset, irValueParameter.type, irValueParameter.symbol), + compilerContext.irBuiltIns.unitType + ) + ) + return irBody + } + + fun generateReceiverExpressionForFieldAccess( + ownerSymbol: IrValueSymbol + ): IrExpression = IrGetValueImpl( + ownerSymbol.owner.startOffset, ownerSymbol.owner.endOffset, + ownerSymbol + ) + + fun IrFunction.createParameterDeclarations( + descriptor: FunctionDescriptor, + overwriteValueParameters: Boolean = false, + copyTypeParameters: Boolean = true + ) { + val function = this + fun irValueParameter(descriptor: ParameterDescriptor): IrValueParameter = with(descriptor) { + @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend + factory.createValueParameter( + function.startOffset, function.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this), + name, indexOrMinusOne, type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline, + isHidden = false, isAssignable = false + ).also { + it.parent = function + } + } + + if (copyTypeParameters) { + assert(typeParameters.isEmpty()) + copyTypeParamsFromDescriptor(descriptor) + } + + dispatchReceiverParameter = descriptor.dispatchReceiverParameter?.let { irValueParameter(it) } + extensionReceiverParameter = descriptor.extensionReceiverParameter?.let { irValueParameter(it) } + + if (!overwriteValueParameters) + assert(valueParameters.isEmpty()) + + valueParameters = descriptor.valueParameters.map { irValueParameter(it) } + } + + fun IrFunction.copyTypeParamsFromDescriptor(descriptor: FunctionDescriptor) { + val newTypeParameters = descriptor.typeParameters.map { + factory.createTypeParameter( + startOffset, endOffset, + SERIALIZABLE_PLUGIN_ORIGIN, + IrTypeParameterSymbolImpl(it), + it.name, it.index, it.isReified, it.variance + ).also { typeParameter -> + typeParameter.parent = this + } + } + @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend + newTypeParameters.forEach { typeParameter -> + typeParameter.superTypes = typeParameter.descriptor.upperBounds.map { it.toIrType() } + } + + typeParameters = newTypeParameters + } + + fun createClassReference(classType: IrType, startOffset: Int, endOffset: Int): IrClassReference { + return IrClassReferenceImpl( + startOffset, + endOffset, + compilerContext.irBuiltIns.kClassClass.starProjectedType, + classType.classifierOrFail, + classType + ) + } + + fun IrBuilderWithScope.classReference(classSymbol: IrType): IrClassReference = + createClassReference(classSymbol, startOffset, endOffset) + + fun collectSerialInfoAnnotations(irClass: IrClass): List { + if (!(irClass.isInterface || irClass.descriptor.hasSerializableOrMetaAnnotation)) return emptyList() + val annotationByFq: MutableMap = + irClass.annotations.associateBy { it.symbol.owner.parentAsClass.descriptor.fqNameSafe }.toMutableMap() + for (clazz in irClass.getAllSuperclasses()) { + val annotations = clazz.annotations + .mapNotNull { + val descriptor = it.symbol.owner.parentAsClass.descriptor + if (descriptor.isInheritableSerialInfoAnnotation) descriptor.fqNameSafe to it else null + } + annotations.forEach { (fqname, call) -> + if (fqname !in annotationByFq) { + annotationByFq[fqname] = call + } else { + // SerializationPluginDeclarationChecker already reported inconsistency + } + } + } + return annotationByFq.values.toList() + } + + fun IrBuilderWithScope.copyAnnotationsFrom(annotations: List): List = + annotations.mapNotNull { annotationCall -> + val annotationClass = annotationCall.symbol.owner.parentAsClass + if (!annotationClass.descriptor.isSerialInfoAnnotation) return@mapNotNull null + + if (compilerContext.platform.isJvm()) { + val implClass = compilerContext.serialInfoImplJvmIrGenerator.getImplClass(annotationClass) + val ctor = implClass.constructors.singleOrNull { it.valueParameters.size == annotationCall.valueArgumentsCount } + ?: error("No constructor args found for SerialInfo annotation Impl class: ${implClass.render()}") + irCall(ctor).apply { + for (i in 0 until annotationCall.valueArgumentsCount) { + val argument = annotationCall.getValueArgument(i) + ?: annotationClass.primaryConstructor!!.valueParameters[i].defaultValue?.expression + putValueArgument(i, argument!!.deepCopyWithVariables()) + } + } + } else { + annotationCall.deepCopyWithVariables() + } + } + + fun IrBuilderWithScope.wrapperClassReference(classType: IrType): IrClassReference { + if (compilerContext.platform.isJvm()) { + // "Byte::class" -> "java.lang.Byte::class" +// TODO: get rid of descriptor + val wrapperFqName = KotlinBuiltIns.getPrimitiveType(classType.classOrNull!!.descriptor)?.let(JvmPrimitiveType::get)?.wrapperFqName + if (wrapperFqName != null) { + val wrapperClass = compilerContext.referenceClass(ClassId.topLevel(wrapperFqName)) + ?: error("Primitive wrapper class for $classType not found: $wrapperFqName") + return classReference(wrapperClass.defaultType) + } + } + return classReference(classType) + } + + fun IrClass.getSuperClassOrAny(): IrClass = getSuperClassNotAny() ?: compilerContext.irBuiltIns.anyClass.owner +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt new file mode 100644 index 00000000000..4c7f718da67 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt @@ -0,0 +1,202 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.serialization.compiler.backend.ir + +import org.jetbrains.kotlin.backend.jvm.ir.getStringConstArgument +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +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.IrTypeParameterSymbol +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages + +internal fun IrType.isKSerializer(): Boolean { + val simpleType = this as? IrSimpleType ?: return false + val classifier = simpleType.classifier as? IrClassSymbol ?: return false + val fqName = classifier.owner.fqNameWhenAvailable + return fqName == SerialEntityNames.KSERIALIZER_NAME_FQ || fqName == SerialEntityNames.GENERATED_SERIALIZER_FQ +} + +internal val IrClass.isInternalSerializable: Boolean + get() { + if (kind != ClassKind.CLASS) return false + return hasSerializableOrMetaAnnotationWithoutArgs() + } + +internal val IrClass.isAbstractOrSealedSerializableClass: Boolean get() = isInternalSerializable && (modality == Modality.ABSTRACT || modality == Modality.SEALED) + +internal val IrClass.isStaticSerializable: Boolean get() = this.typeParameters.isEmpty() + + +internal val IrClass.hasCompanionObjectAsSerializer: Boolean + get() = isInternallySerializableObject || companionObject()?.serializerForClass == this.symbol + +internal val IrClass.isInternallySerializableObject: Boolean + get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs() + + +internal fun IrClass.findPluginGeneratedMethod(name: String): IrSimpleFunction? { + return this.functions.find { + it.name.asString() == name && it.isFromPlugin() + } +} + +internal fun IrClass.isEnumWithLegacyGeneratedSerializer(): Boolean = isInternallySerializableEnum() && useGeneratedEnumSerializer + +internal val IrClass.useGeneratedEnumSerializer: Boolean + get() = true // FIXME This would break if we try to use this new IR compiler with pre-1.4.1 serialization versions. I think we just need to raise min runtime version. + +internal val IrClass.isSealedSerializableInterface: Boolean + get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotation() + +internal fun IrClass.isInternallySerializableEnum(): Boolean = + kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs() + +fun IrType.isGeneratedSerializableObject(): Boolean { + return classOrNull?.run { owner.kind == ClassKind.OBJECT && owner.hasSerializableOrMetaAnnotationWithoutArgs() } == true +} + +internal val IrClass.isSerializableObject: Boolean + get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotation() + +internal fun IrClass.hasSerializableOrMetaAnnotationWithoutArgs(): Boolean = checkSerializableOrMetaAnnotationArgs(mustDoNotHaveArgs = true) + +fun IrClass.hasSerializableOrMetaAnnotation() = checkSerializableOrMetaAnnotationArgs(mustDoNotHaveArgs = false) + +private fun IrClass.checkSerializableOrMetaAnnotationArgs(mustDoNotHaveArgs: Boolean): Boolean { + val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) + if (annot != null) { // @Serializable have higher priority + if (!mustDoNotHaveArgs) return true + if (annot.getValueArgument(0) != null) return false + return true + } + return annotations + .map { it.annotationClass.annotations } + .any { it.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) } +} + +internal val IrClass.isSerialInfoAnnotation: Boolean + get() = annotations.hasAnnotation(SerializationAnnotations.serialInfoFqName) + || annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName) + || annotations.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) + +internal val IrClass.shouldHaveGeneratedSerializer: Boolean + get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) + || isEnumWithLegacyGeneratedSerializer() + +internal val IrClass.shouldHaveGeneratedMethodsInCompanion: Boolean + get() = this.isSerializableObject || this.isSerializableEnum() || (this.kind == ClassKind.CLASS && hasSerializableOrMetaAnnotation()) || this.isSealedSerializableInterface + +internal fun IrClass.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotation() + +internal val IrType.genericIndex: Int? + get() = (this.classifierOrNull as? IrTypeParameterSymbol)?.owner?.index + +fun IrType.serialName(): String = this.classOrNull!!.owner.serialName() + +fun IrClass.serialName(): String { + return annotations.serialNameValue ?: fqNameWhenAvailable?.asString() ?: error("${this.render()} does not have fqName") +} + +fun IrClass.findEnumValuesMethod() = this.functions.singleOrNull { f -> + f.name == Name.identifier("values") && f.valueParameters.isEmpty() && f.extensionReceiverParameter == null +} ?: throw AssertionError("Enum class does not have single .values() function") + +internal fun IrClass.enumEntries(): List { + check(this.kind == ClassKind.ENUM_CLASS) + return declarations.filterIsInstance().toList() +} + +internal fun IrClass.isEnumWithSerialInfoAnnotation(): Boolean { + if (kind != ClassKind.ENUM_CLASS) return false + if (annotations.hasAnySerialAnnotation) return true + return enumEntries().any { (it.annotations.hasAnySerialAnnotation) } +} + +fun IrClass.findWriteSelfMethod(): IrSimpleFunction? = + functions.singleOrNull { it.name == SerialEntityNames.WRITE_SELF_NAME && !it.isFakeOverride } + +fun IrClass.getSuperClassNotAny(): IrClass? { + val parentClass = + superTypes + .mapNotNull { it.classOrNull?.owner } + .singleOrNull { it.kind == ClassKind.CLASS || it.kind == ClassKind.ENUM_CLASS } ?: return null + return if (parentClass.defaultType.isAny()) null else parentClass +} + +internal fun IrDeclaration.isFromPlugin(): Boolean = + this.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) || (this.descriptor as? CallableMemberDescriptor)?.kind == CallableMemberDescriptor.Kind.SYNTHESIZED // old FE doesn't specify origin + +internal fun IrConstructor.isSerializationCtor(): Boolean { + /*kind == CallableMemberDescriptor.Kind.SYNTHESIZED does not work because DeserializedClassConstructorDescriptor loses its kind*/ + return valueParameters.lastOrNull()?.run { + name == SerialEntityNames.dummyParamName && type.classFqName == SerializationPackages.internalPackageFqName.child( + SerialEntityNames.SERIAL_CTOR_MARKER_NAME + ) + } == true +} + + +internal fun IrConstructor.lastArgumentIsAnnotationArray(): Boolean { + val lastArgType = valueParameters.lastOrNull()?.type + if (lastArgType == null || !lastArgType.isArray()) return false + return ((lastArgType as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull?.classFqName?.toString() == "kotlin.Annotation") +} + +fun IrClass.serializableSyntheticConstructor(): IrConstructorSymbol? { // FIXME: remove nullability W/A when FIR plugin will add proper deserialization ctor + return declarations.filterIsInstance().singleOrNull { it.isSerializationCtor() }?.symbol +} + +internal fun IrExpression.isInitializePropertyFromParameter(): Boolean = + this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER + +internal val IrConstructorCall.annotationClass + get() = this.symbol.owner.constructedClass + +internal val List.hasAnySerialAnnotation: Boolean + get() = serialNameValue != null || any { it.annotationClass.isSerialInfoAnnotation == true } + +internal val List.serialNameValue: String? + get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)?.getStringConstArgument(0) // @SerialName("foo") + +/** + * True — ALWAYS + * False — NEVER + * null — not specified + */ +fun IrProperty.getEncodeDefaultAnnotationValue(): Boolean? { + val call = annotations.findAnnotation(SerializationAnnotations.encodeDefaultFqName) ?: return null + val arg = call.getValueArgument(0) ?: return true // ALWAYS by default + val argValue = (arg as? IrGetEnumValue + ?: error("Argument of enum constructor expected to implement IrGetEnumValue, got $arg")).symbol.owner.name.toString() + return when (argValue) { + "ALWAYS" -> true + "NEVER" -> false + else -> error("Unknown EncodeDefaultMode enum value: $argValue") + } +} + +fun findSerializerConstructorForTypeArgumentsSerializers(serializer: IrClass): IrConstructorSymbol? { + val typeParamsCount = ((serializer.superTypes.find { it.isKSerializer() } as IrSimpleType).arguments.first().typeOrNull!! as IrSimpleType).arguments.size + if (typeParamsCount == 0) return null //don't need it + + return serializer.constructors.singleOrNull { + it.valueParameters.let { vps -> vps.size == typeParamsCount && vps.all { vp -> vp.type.isKSerializer() } } + }?.symbol +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrSerializableProperties.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrSerializableProperties.kt new file mode 100644 index 00000000000..97538e7d825 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrSerializableProperties.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.serialization.compiler.backend.ir + +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.util.hasAnnotation +import org.jetbrains.kotlin.ir.util.hasDefaultValue +import org.jetbrains.kotlin.ir.util.primaryConstructor +import org.jetbrains.kotlin.ir.util.properties +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor +import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin +import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext +import org.jetbrains.kotlinx.serialization.compiler.resolve.* + +class IrSerializableProperty( + val ir: IrProperty, + override val isConstructorParameterWithDefault: Boolean, + hasBackingField: Boolean, + declaresDefaultValue: Boolean +) : ISerializableProperty { + override val name = ir.annotations.serialNameValue ?: ir.name.asString() + override val originalDescriptorName: Name = ir.name + val type = ir.getter!!.returnType as IrSimpleType + val genericIndex = type.genericIndex + fun serializableWith(ctx: SerializationPluginContext) = ir.annotations.serializableWith() ?: analyzeSpecialSerializers(ctx, ir.annotations) + override val optional = !ir.annotations.hasAnnotation(SerializationAnnotations.requiredAnnotationFqName) && declaresDefaultValue + override val transient = ir.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName) || !hasBackingField +} + +class IrSerializableProperties( + override val serializableProperties: List, + override val isExternallySerializable: Boolean, + override val serializableConstructorProperties: List, + override val serializableStandaloneProperties: List +) : ISerializableProperties + +internal fun serializablePropertiesForIrBackend( + irClass: IrClass, + serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null +): IrSerializableProperties { + val properties = irClass.properties.toList() + val primaryConstructorParams = irClass.primaryConstructor?.valueParameters.orEmpty() + val primaryParamsAsProps = properties.associateBy { it.name }.let { namesMap -> + primaryConstructorParams.mapNotNull { + if (it.name !in namesMap) null else namesMap.getValue(it.name) to it.hasDefaultValue() + }.toMap() + } + + fun isPropSerializable(it: IrProperty) = + if (irClass.isInternalSerializable) !it.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName) + else !DescriptorVisibilities.isPrivate(it.visibility) && ((it.isVar && !it.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName)) || primaryParamsAsProps.contains( + it + )) + + val (primaryCtorSerializableProps, bodySerializableProps) = properties + .asSequence() + .filter { !it.isFakeOverride && !it.isDelegated } + .filter(::isPropSerializable) + .map { + val isConstructorParameterWithDefault = primaryParamsAsProps[it] ?: false + // FIXME: workaround because IrLazyProperty doesn't deserialize information about backing fields. Fallback to descriptor won't work with FIR. + val isPropertyFromAnotherModuleDeclaresDefaultValue = it.descriptor is DeserializedPropertyDescriptor && it.descriptor.declaresDefaultValue() + val isPropertyWithBackingFieldFromAnotherModule = it.descriptor is DeserializedPropertyDescriptor && (it.descriptor.backingField != null || isPropertyFromAnotherModuleDeclaresDefaultValue) + IrSerializableProperty( + it, + isConstructorParameterWithDefault, + it.backingField != null || isPropertyWithBackingFieldFromAnotherModule, + it.backingField?.initializer.let { init -> init != null && !init.expression.isInitializePropertyFromParameter() } || isConstructorParameterWithDefault + || isPropertyFromAnotherModuleDeclaresDefaultValue + ) + } + .filterNot { it.transient } + .partition { primaryParamsAsProps.contains(it.ir) } + + var serializableProps = run { + val supers = irClass.getSuperClassNotAny() + if (supers == null || !supers.isInternalSerializable) + primaryCtorSerializableProps + bodySerializableProps + else + serializablePropertiesForIrBackend( + supers, + serializationDescriptorSerializer + ).serializableProperties + primaryCtorSerializableProps + bodySerializableProps + } + + // FIXME: since descriptor from FIR does not have classProto in it(?), this line won't do anything + serializableProps = restoreCorrectOrderFromClassProtoExtension(irClass.descriptor, serializableProps) + + val isExternallySerializable = + irClass.isInternallySerializableEnum() || primaryConstructorParams.size == primaryParamsAsProps.size + + return IrSerializableProperties(serializableProps, isExternallySerializable, primaryCtorSerializableProps, bodySerializableProps) +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt index b5b61c9bd52..ac81e449df7 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt @@ -42,7 +42,7 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames class SerialInfoImplJvmIrGenerator( private val context: SerializationPluginContext, private val moduleFragment: IrModuleFragment, -) : IrBuilderExtension { +) : IrBuilderWithPluginContext { override val compilerContext: SerializationPluginContext get() = context 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 5b57523f523..ec20e8030bc 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 @@ -6,7 +6,6 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irInt @@ -21,41 +20,37 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlinx.serialization.compiler.backend.common.AbstractIrGenerator -import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer -import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerializableClassByCompanion -import org.jetbrains.kotlinx.serialization.compiler.backend.common.isKSerializer import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext -import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages +import org.jetbrains.kotlinx.serialization.compiler.resolve.needSerializerFactory class SerializableCompanionIrGenerator( val irClass: IrClass, val serializableIrClass: IrClass, - override val compilerContext: SerializationPluginContext, -) : AbstractIrGenerator(irClass), IrBuilderExtension { + compilerContext: SerializationPluginContext, +) : BaseIrGenerator(irClass, compilerContext) { - private val serializableDescriptor = serializableIrClass.descriptor - - private fun getSerializerGetterDescriptor(): FunctionDescriptor { + private fun getSerializerGetterFunction(): IrSimpleFunction { return irClass.findDeclaration { - (it.valueParameters.size == serializableDescriptor.declaredTypeParameters.size - && it.valueParameters.all { p -> isKSerializer(p.type) }) && isKSerializer(it.returnType) - }?.descriptor ?: throw IllegalStateException( + (it.valueParameters.size == serializableIrClass.typeParameters.size + && it.valueParameters.all { p -> p.type.isKSerializer() }) && it.returnType.isKSerializer() + } ?: throw IllegalStateException( "Can't find synthesized 'Companion.serializer()' function to generate, " + "probably clash with user-defined function has occurred" ) } fun generate() { - val serializerGetterDescriptor = getSerializerGetterDescriptor() + val serializerGetterFunction = getSerializerGetterFunction() - if (serializableDescriptor.isSerializableObject - || serializableDescriptor.isAbstractOrSealedSerializableClass() - || serializableDescriptor.isSerializableEnum() + if (serializableIrClass.isSerializableObject + || serializableIrClass.isAbstractOrSealedSerializableClass + || serializableIrClass.isSerializableEnum() ) { - generateLazySerializerGetter(serializerGetterDescriptor) + generateLazySerializerGetter(serializerGetterFunction) } else { - generateSerializerGetter(serializerGetterDescriptor) + generateSerializerGetter(serializerGetterFunction) } } @@ -64,12 +59,11 @@ class SerializableCompanionIrGenerator( irClass: IrClass, context: SerializationPluginContext, ) { - val companionDescriptor = irClass.descriptor - val serializableClass = getSerializableClassDescriptorByCompanion(companionDescriptor) ?: return + val companionDescriptor = irClass + val serializableClass = getSerializableClassByCompanion(companionDescriptor) ?: return if (serializableClass.shouldHaveGeneratedMethodsInCompanion) { SerializableCompanionIrGenerator(irClass, getSerializableClassByCompanion(irClass)!!, context).generate() - val declaration = irClass.constructors.primary // todo: move to appropriate place - if (declaration.body == null) declaration.body = context.generateBodyForDefaultConstructor(declaration) + irClass.addDefaultConstructorIfAbsent(context) irClass.patchDeclarationParents(irClass.parent) } } @@ -110,7 +104,7 @@ class SerializableCompanionIrGenerator( irSerializableClass.annotations += annotationCtorCall } - fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) { + fun generateLazySerializerGetter(methodDescriptor: IrSimpleFunction) { val serializer = requireNotNull( findTypeSerializer( compilerContext, @@ -124,21 +118,20 @@ class SerializableCompanionIrGenerator( val property = createLazyProperty(irClass, targetIrType, SerialEntityNames.CACHED_SERIALIZER_PROPERTY_NAME) { val expr = serializerInstance( - this@SerializableCompanionIrGenerator, serializer, compilerContext, serializableIrClass.defaultType ) patchSerializableClassWithMarkerAnnotation(kSerializerIrClass) +irReturn(requireNotNull(expr)) } - irClass.contributeFunction(methodDescriptor) { + addFunctionBody(methodDescriptor) { +irReturn(getLazyValueExpression(it.dispatchReceiverParameter!!, property, targetIrType)) } generateSerializerFactoryIfNeeded(methodDescriptor) } - fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) { - irClass.contributeFunction(methodDescriptor) { getter -> + fun generateSerializerGetter(methodDescriptor: IrSimpleFunction) { + addFunctionBody(methodDescriptor) { getter -> val serializer = requireNotNull( findTypeSerializer( compilerContext, @@ -147,7 +140,6 @@ class SerializableCompanionIrGenerator( ) val args: List = getter.valueParameters.map { irGet(it) } val expr = serializerInstance( - this@SerializableCompanionIrGenerator, serializer, compilerContext, serializableIrClass.defaultType ) { it, _ -> args[it] } @@ -157,25 +149,25 @@ class SerializableCompanionIrGenerator( generateSerializerFactoryIfNeeded(methodDescriptor) } - private fun generateSerializerFactoryIfNeeded(getterDescriptor: FunctionDescriptor) { + private fun generateSerializerFactoryIfNeeded(getterDescriptor: IrSimpleFunction) { if (!irClass.descriptor.needSerializerFactory()) return val serialFactoryDescriptor = irClass.findDeclaration { it.valueParameters.size == 1 && it.valueParameters.first().isVararg -// && it.kind == CallableMemberDescriptor.Kind.SYNTHESIZED - && isKSerializer(it.returnType) + && it.returnType.isKSerializer() + && it.isFromPlugin() } ?: return addFunctionBody(serialFactoryDescriptor) { factory -> val kSerializerStarType = factory.returnType val array = factory.valueParameters.first() - val argsSize = serializableDescriptor.declaredTypeParameters.size + val argsSize = serializableIrClass.typeParameters.size val arrayGet = compilerContext.irBuiltIns.arrayClass.owner.declarations.filterIsInstance() .single { it.name.asString() == "get" } val serializers: List = (0 until argsSize).map { irInvoke(irGet(array), arrayGet.symbol, irInt(it), typeHint = kSerializerStarType) } - val serializerCall = compilerContext.symbolTable.referenceSimpleFunction(getterDescriptor) + val serializerCall = getterDescriptor.symbol val call = irInvoke( IrGetValueImpl(startOffset, endOffset, factory.dispatchReceiverParameter!!.symbol), serializerCall, 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 b974b207b97..6fd0667616c 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 @@ -24,7 +24,6 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.getOrPutNullable -import org.jetbrains.kotlinx.serialization.compiler.backend.common.* import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* @@ -36,8 +35,8 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SE class SerializableIrGenerator( val irClass: IrClass, - override val compilerContext: SerializationPluginContext -) : AbstractIrGenerator(irClass), IrBuilderExtension { + compilerContext: SerializationPluginContext +) : BaseIrGenerator(irClass, compilerContext) { protected val properties = serializablePropertiesForIrBackend(irClass) @@ -65,7 +64,7 @@ class SerializableIrGenerator( val thiz = irClass.thisReceiver!! val serializableProperties = properties.serializableProperties - val serialDescs = serializableProperties.map { it.descriptor }.toSet() + val serialDescs = serializableProperties.map { it.ir }.toSet() val propertyByParamReplacer: (ValueParameterDescriptor) -> IrExpression? = createPropertyByParamReplacer(irClass, serializableProperties, thiz) @@ -137,19 +136,19 @@ class SerializableIrGenerator( val paramRef = ctor.valueParameters[index + seenVarsOffset] // Assign this.a = a in else branch // Set field directly w/o setter to match behavior of old backend plugin - val backingFieldToAssign = prop.descriptor.backingField!! + val backingFieldToAssign = prop.ir.backingField!! val assignParamExpr = irSetField(irGet(thiz), backingFieldToAssign, irGet(paramRef)) val ifNotSeenExpr: IrExpression = if (prop.optional) { val initializerBody = - requireNotNull(initializerAdapter(prop.irField?.initializer!!)) { "Optional value without an initializer" } // todo: filter abstract here + requireNotNull(initializerAdapter(prop.ir.backingField?.initializer!!)) { "Optional value without an initializer" } // todo: filter abstract here irSetField(irGet(thiz), backingFieldToAssign, initializerBody) } else { // property required if (useFieldMissingOptimization()) { // field definitely not empty as it's checked before - no need another IF, only assign property from param +assignParamExpr - statementsAfterSerializableProperty[prop.descriptor]?.forEach { +it } + statementsAfterSerializableProperty[prop.ir]?.forEach { +it } continue } else { irThrow(irInvoke(null, exceptionCtorRef, irString(prop.name), typeHint = exceptionType)) @@ -168,7 +167,7 @@ class SerializableIrGenerator( +irIfThenElse(compilerContext.irBuiltIns.unitType, propNotSeenTest, ifNotSeenExpr, assignParamExpr) - statementsAfterSerializableProperty[prop.descriptor]?.forEach { +it } + statementsAfterSerializableProperty[prop.ir]?.forEach { +it } } // Handle function-intialized interface delegates @@ -219,7 +218,7 @@ class SerializableIrGenerator( } private fun IrBlockBodyBuilder.getInstantiateDescriptorExpr(): IrExpression { - val classConstructors = serialDescriptorImplClass.constructors //todo: remove reference + val classConstructors = serialDescriptorImplClass.constructors val serialClassDescImplCtor = classConstructors.single { it.isPrimary }.symbol return irInvoke( null, serialClassDescImplCtor, @@ -271,7 +270,7 @@ class SerializableIrGenerator( propertiesStart: Int ): Int { check(superClass.isInternalSerializable) - val superCtorRef = serializableSyntheticConstructor(superClass)!! + val superCtorRef = superClass.serializableSyntheticConstructor()!! val superProperties = serializablePropertiesForIrBackend(superClass).serializableProperties val superSlots = superProperties.bitMaskSlotCount() val arguments = allValueParameters.subList(0, superSlots) + @@ -332,7 +331,6 @@ class SerializableIrGenerator( val genericIdx = irClass.defaultType.arguments.indexOf(arg).let { if (it == -1) null else it } val serial = findTypeSerializerOrContext(compilerContext, arg.typeOrNull!!) serializerInstance( - this@SerializableIrGenerator, serial, compilerContext, arg.typeOrNull!!, @@ -346,9 +344,9 @@ class SerializableIrGenerator( } serializeAllProperties( - this@SerializableIrGenerator, irClass, serializableProperties, - objectToSerialize, localOutput, localSerialDesc, - kOutputClass, ignoreIndexTo, initializerAdapter + serializableProperties, objectToSerialize, + localOutput, localSerialDesc, kOutputClass, + ignoreIndexTo, initializerAdapter ) { it, _ -> irGet(writeSelfFunction.valueParameters[3 + it]) } 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 51103703eb3..687bfd43695 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 @@ -7,8 +7,6 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.backend.jvm.functionByName import org.jetbrains.kotlin.builtins.StandardNames -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction @@ -23,14 +21,9 @@ import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.util.properties import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlinx.serialization.compiler.backend.common.enumEntries -import org.jetbrains.kotlinx.serialization.compiler.backend.common.getClassFromInternalSerializationPackage -import org.jetbrains.kotlinx.serialization.compiler.backend.common.getClassFromRuntime -import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialNameValue import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext -import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.CallingConventions +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames class SerializerForEnumsGenerator( irClass: IrClass, 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 a94c3335383..9327fc13302 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 @@ -6,8 +6,6 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.backend.jvm.functionByName -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction @@ -18,12 +16,9 @@ import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.typeOrNull import org.jetbrains.kotlin.ir.util.constructors -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlinx.serialization.compiler.backend.common.getClassFromInternalSerializationPackage -import org.jetbrains.kotlinx.serialization.compiler.backend.common.getClassFromRuntime import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext -import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.CallingConventions +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames class SerializerForInlineClassGenerator( irClass: IrClass, @@ -44,7 +39,7 @@ class SerializerForInlineClassGenerator( val inlineEncoder = irTemporary(encodeInlineCall, nameHint = "inlineEncoder") val property = serializableProperties.first() - val value = getFromBox(irGet(saveFunc.valueParameters[1]), property) + val value = getProperty(irGet(saveFunc.valueParameters[1]), property.ir) // inlineEncoder.encodeInt/String/SerializableValue val elementCall = formEncodeDecodePropertyCall(irGet(inlineEncoder), saveFunc.dispatchReceiverParameter!!, property, {innerSerial, sti -> @@ -109,6 +104,4 @@ class SerializerForInlineClassGenerator( listOf(expression) ) - private fun IrBlockBodyBuilder.getFromBox(expression: IrExpression, serializableProperty: IrSerializableProperty): IrExpression = - getProperty(expression, serializableProperty.descriptor) } 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 75c45878c9c..503417e9929 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 @@ -33,11 +33,9 @@ import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue import org.jetbrains.kotlin.resolve.isInlineClass import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.cast -import org.jetbrains.kotlinx.serialization.compiler.backend.common.* import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* @@ -55,10 +53,10 @@ internal typealias FunctionWithArgs = Pair> open class SerializerIrGenerator( val irClass: IrClass, - final override val compilerContext: SerializationPluginContext, + compilerContext: SerializationPluginContext, metadataPlugin: SerializationDescriptorSerializerPlugin?, private val serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, -) : AbstractIrGenerator(irClass), IrBuilderExtension { +) : BaseIrGenerator(irClass, compilerContext) { protected val serializableIrClass = getSerializableClassDescriptorBySerializer(irClass)!! protected val serialName: String = serializableIrClass.serialName() @@ -91,7 +89,7 @@ open class SerializerIrGenerator( if (count == 0) return emptyList() val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" } return propNames.mapNotNull { name -> - getProperty(name) { isKSerializer(it.getter!!.returnType) } + getProperty(name) { it.getter!!.returnType.isKSerializer() } } } @@ -144,8 +142,7 @@ open class SerializerIrGenerator( // save local descriptor to field +irSetField( generateReceiverExpressionForFieldAccess( - thisAsReceiverParameter.symbol, - generatedSerialDescPropertyDescriptor.descriptor + thisAsReceiverParameter.symbol ), prop.backingField!!, irGet(localDesc) @@ -184,7 +181,7 @@ open class SerializerIrGenerator( if (classProp.transient) continue +addFieldCall(classProp) // add property annotations - val property = classProp.descriptor//.getIrPropertyFrom(serializableIrClass) + val property = classProp.ir//.getIrPropertyFrom(serializableIrClass) copySerialInfoAnnotationsToDescriptor( property.annotations, localDescriptor, @@ -224,8 +221,7 @@ open class SerializerIrGenerator( val localSerial = localSerializersFieldsDescriptors[index].second.backingField!! +irSetField( generateReceiverExpressionForFieldAccess( - thisAsReceiverParameter.symbol, - localSerializersFieldsDescriptors[index].first + thisAsReceiverParameter.symbol ), localSerial, irGet(param) ) } @@ -305,8 +301,8 @@ open class SerializerIrGenerator( createInitializerAdapter(serializableIrClass, propertyByParamReplacer, thisSymbol to { irGet(objectToSerialize) }) serializeAllProperties( - this@SerializerIrGenerator, serializableIrClass, serializableProperties, objectToSerialize, - localOutput, localSerialDesc, kOutputClass, ignoreIndexTo = -1, initializerAdapter + serializableProperties, objectToSerialize, localOutput, + localSerialDesc, kOutputClass, ignoreIndexTo = -1, initializerAdapter ) { it, _ -> val (_, ir) = localSerializersFieldsDescriptors[it] irGetField(irGet(saveFunc.dispatchReceiverParameter!!), ir.backingField!!) @@ -326,7 +322,6 @@ open class SerializerIrGenerator( whenDoNot: (sti: IrSerialTypeInfo) -> FunctionWithArgs, returnTypeHint: IrType? = null ): IrExpression = formEncodeDecodePropertyCall( - this@SerializerIrGenerator, encoder, property, whenHaveSerializer, @@ -385,7 +380,7 @@ open class SerializerIrGenerator( val serialPropertiesIndexes = serializableProperties .mapIndexed { i, property -> property to i } - .associate { (p, i) -> p.descriptor to i } + .associate { (p, i) -> p.ir to i } val transients = serializableIrClass.declarations.asSequence() .filterIsInstance() @@ -395,7 +390,7 @@ open class SerializerIrGenerator( // var bitMask0 = 0, bitMask1 = 0... val bitMasks = (0 until blocksCnt).map { irTemporary(irInt(0), "bitMask$it", isMutable = true) } // var local0 = null, local1 = null ... - val serialPropertiesMap = serializableProperties.mapIndexed { i, prop -> i to prop.descriptor }.associate { (i, descriptor) -> + val serialPropertiesMap = serializableProperties.mapIndexed { i, prop -> i to prop.ir }.associate { (i, descriptor) -> val (expr, type) = defaultValueAndType(descriptor) descriptor to irTemporary(expr, "local$i", type, isMutable = true) } @@ -425,7 +420,7 @@ open class SerializerIrGenerator( it.owner.name.asString() == "${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}" && it.owner.valueParameters.size == 4 } to listOf( - localSerialDesc.get(), irInt(index), innerSerial, serialPropertiesMap.getValue(property.descriptor).get() + localSerialDesc.get(), irInt(index), innerSerial, serialPropertiesMap.getValue(property.ir).get() ) }, {sti -> inputClass.functions.single { @@ -435,7 +430,7 @@ open class SerializerIrGenerator( }, returnTypeHint = property.type) // local$i = localInput.decode...(...) +irSet( - serialPropertiesMap.getValue(property.descriptor).symbol, + serialPropertiesMap.getValue(property.ir).symbol, decodeFuncToCall ) // bitMask[i] |= 1 << x @@ -492,9 +487,9 @@ open class SerializerIrGenerator( ) val typeArgs = (loadFunc.returnType as IrSimpleType).arguments.map { (it as IrTypeProjection).type } - val deserCtor: IrConstructorSymbol? = serializableSyntheticConstructor(serializableIrClass) + val deserCtor: IrConstructorSymbol? = serializableIrClass.serializableSyntheticConstructor() if (serializableIrClass.isInternalSerializable && deserCtor != null) { - var args: List = serializableProperties.map { serialPropertiesMap.getValue(it.descriptor).get() } + var args: List = serializableProperties.map { serialPropertiesMap.getValue(it.ir).get() } args = bitMasks.map { irGet(it) } + args + irNull() +irReturn(irInvoke(null, deserCtor, typeArgs, args)) } else { @@ -526,14 +521,13 @@ open class SerializerIrGenerator( // constructor args: val ctorArgs = params.map { parameter -> - val propertyDescriptor = serializableIrClass.properties.find { it.name == parameter.name }!! // todo: check with tests - val parameterDescriptor = parameter.descriptor as ValueParameterDescriptor // TODO: remove descriptor here + val propertyDescriptor = serializableIrClass.properties.find { it.name == parameter.name }!! val serialProperty = serialPropertiesMap[propertyDescriptor] // null if transient if (serialProperty != null) { val index = serialPropertiesIndexes.getValue(propertyDescriptor) - if (parameterDescriptor.hasDefaultValue()) { + if (parameter.hasDefaultValue()) { val propNotSeenTest = irEquals( irInt(0), @@ -552,7 +546,7 @@ open class SerializerIrGenerator( serialProperty.get() } else { val transientVar = transientsPropertiesMap.getValue(propertyDescriptor) - if (parameterDescriptor.hasDefaultValue()) { + if (parameter.hasDefaultValue()) { val defaultValueExp = parameter.defaultValue!! val expr = initializerAdapter(defaultValueExp) +irSet(transientVar.symbol, expr) @@ -574,9 +568,9 @@ open class SerializerIrGenerator( bitMasks: List ) { for (property in properties.serializableStandaloneProperties) { - val localPropIndex = propIndexes(property.descriptor) + val localPropIndex = propIndexes(property.ir) // generate setter call - val setter = property.descriptor.setter!! + val setter = property.ir.setter!! val propSeenTest = irNotEquals( irInt(0), @@ -587,13 +581,12 @@ open class SerializerIrGenerator( ) ) - val setterInvokeExpr = irSet(setter.returnType, irGet(serializableVar), setter.symbol, irGet(propVars(property.descriptor))) + val setterInvokeExpr = irSet(setter.returnType, irGet(serializableVar), setter.symbol, irGet(propVars(property.ir))) +irIfThen(propSeenTest, setterInvokeExpr) } } - // !!! TODO: this doesn't work with OLD FE !!! fun generate() { val prop = generatedSerialDescPropertyDescriptor?.let { generateSerializableClassProperty(it); true } ?: false if (prop) @@ -631,8 +624,7 @@ open class SerializerIrGenerator( else -> SerializerIrGenerator(irClass, context, metadataPlugin, serialInfoJvmGenerator) } generator.generate() - val declaration = irClass.constructors.primary // todo: move to appropriate place - if (declaration.body == null) declaration.body = context.generateBodyForDefaultConstructor(declaration) + irClass.addDefaultConstructorIfAbsent(context) irClass.patchDeclarationParents(irClass.parent) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt similarity index 57% rename from plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt rename to plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt index a10c7c09717..048f8370b5f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt @@ -3,31 +3,79 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package org.jetbrains.kotlinx.serialization.compiler.backend.common +package org.jetbrains.kotlinx.serialization.compiler.backend.ir -import org.jetbrains.kotlin.backend.jvm.ir.getStringConstArgument -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.expressions.IrClassReference import org.jetbrains.kotlin.ir.expressions.IrConstructorCall -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlinx.serialization.compiler.backend.common.findStandardKotlinTypeSerializer import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.* import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext -import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey -import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages +import org.jetbrains.kotlinx.serialization.compiler.resolve.SpecialBuiltins -fun AbstractIrGenerator.findTypeSerializerOrContextUnchecked( +class IrSerialTypeInfo( + val property: IrSerializableProperty, + val elementMethodPrefix: String, + val serializer: IrClassSymbol? = null +) + +fun BaseIrGenerator.getIrSerialTypeInfo(property: IrSerializableProperty, ctx: SerializationPluginContext): IrSerialTypeInfo { + fun SerializableInfo(serializer: IrClassSymbol?) = + IrSerialTypeInfo(property, if (property.type.isNullable()) "Nullable" else "", serializer) + + val T = property.type + property.serializableWith(ctx)?.let { return SerializableInfo(it) } + findAddOnSerializer(T, ctx)?.let { return SerializableInfo(it) } + T.overridenSerializer?.let { return SerializableInfo(it) } + return when { + T.isTypeParameter() -> IrSerialTypeInfo(property, if (property.type.isMarkedNullable()) "Nullable" else "", null) + T.isPrimitiveType() -> IrSerialTypeInfo( + property, + T.classFqName!!.asString().removePrefix("kotlin.") + ) + T.isString() -> IrSerialTypeInfo(property, "String") + T.isArray() -> { + val serializer = property.serializableWith(ctx) ?: ctx.getClassFromInternalSerializationPackage(SpecialBuiltins.referenceArraySerializer) + SerializableInfo(serializer) + } + else -> { + val serializer = + findTypeSerializerOrContext(ctx, property.type) + SerializableInfo(serializer) + } + } +} + +fun BaseIrGenerator.findAddOnSerializer(propertyType: IrType, ctx: SerializationPluginContext): IrClassSymbol? { + val classSymbol = propertyType.classOrNull ?: return null + additionalSerializersInScopeOfCurrentFile[classSymbol to propertyType.isNullable()]?.let { return it } + if (classSymbol in contextualKClassListInCurrentFile) + return ctx.getClassFromRuntime(SpecialBuiltins.contextSerializer) + if (classSymbol.owner.annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName)) + return ctx.getClassFromRuntime(SpecialBuiltins.polymorphicSerializer) + if (propertyType.isNullable()) return findAddOnSerializer(propertyType.makeNotNull(), ctx) + return null +} + +fun BaseIrGenerator.findTypeSerializerOrContext( + context: SerializationPluginContext, kType: IrType +): IrClassSymbol? { + if (kType.isTypeParameter()) return null + return findTypeSerializerOrContextUnchecked(context, kType) ?: error("Serializer for element of type ${kType.render()} has not been found") +} + +fun BaseIrGenerator.findTypeSerializerOrContextUnchecked( context: SerializationPluginContext, kType: IrType ): IrClassSymbol? { val annotations = kType.annotations @@ -53,12 +101,6 @@ fun analyzeSpecialSerializers( else -> null } -fun AbstractIrGenerator.findTypeSerializerOrContext( - context: SerializationPluginContext, kType: IrType -): IrClassSymbol? { - if (kType.isTypeParameter()) return null - return findTypeSerializerOrContextUnchecked(context, kType) ?: error("Serializer for element of type ${kType.render()} has not been found") -} fun findTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? { type.overridenSerializer?.let { return it } @@ -73,6 +115,12 @@ fun findTypeSerializer(context: SerializationPluginContext, type: IrType): IrCla ) return type.classOrNull?.owner.classSerializer(context) // check for serializer defined on the type } +fun findEnumTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? { + val classSymbol = type.classOrNull?.owner ?: return null + return if (classSymbol.kind == ClassKind.ENUM_CLASS && !classSymbol.isEnumWithLegacyGeneratedSerializer()) + context.referenceClass(enumSerializerId) + else null +} internal fun IrClass?.classSerializer(context: SerializationPluginContext): IrClassSymbol? = this?.let { // serializer annotation on class? @@ -91,15 +139,6 @@ internal fun IrClass?.classSerializer(context: SerializationPluginContext): IrCl return null } -private val IrClass.isSerialInfoAnnotation: Boolean - get() = annotations.hasAnnotation(SerializationAnnotations.serialInfoFqName) - || annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName) - || annotations.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) - -internal val IrClass.shouldHaveGeneratedSerializer: Boolean - get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) - || isEnumWithLegacyGeneratedSerializer() - internal fun IrClass.polymorphicSerializerIfApplicableAutomatically(context: SerializationPluginContext): IrClassSymbol? { val serializer = when { kind == ClassKind.INTERFACE && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer @@ -117,45 +156,20 @@ internal fun IrClass.polymorphicSerializerIfApplicableAutomatically(context: Ser } } -internal val IrClass.isInternalSerializable: Boolean +internal val IrType.overridenSerializer: IrClassSymbol? get() { - if (kind != ClassKind.CLASS) return false - return hasSerializableOrMetaAnnotationWithoutArgs() + val desc = this.classOrNull ?: return null + desc.owner.serializableWith?.let { return it } + return null } -internal val IrClass.isAbstractOrSealedSerializableClass: Boolean get() = isInternalSerializable && (modality == Modality.ABSTRACT || modality == Modality.SEALED) - -internal val IrClass.isStaticSerializable: Boolean get() = this.typeParameters.isEmpty() - - -internal val IrClass.hasCompanionObjectAsSerializer: Boolean - get() = isInternallySerializableObject || companionObject()?.serializerForClass == this.symbol - -internal val IrClass.isInternallySerializableObject: Boolean - get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs() +internal val IrClass.serializableWith: IrClassSymbol? + get() = annotations.serializableWith() internal val IrClass.serializerForClass: IrClassSymbol? get() = (annotations.findAnnotation(SerializationAnnotations.serializerAnnotationFqName) ?.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol -fun findEnumTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? { - val classSymbol = type.classOrNull?.owner ?: return null - return if (classSymbol.kind == ClassKind.ENUM_CLASS && !classSymbol.isEnumWithLegacyGeneratedSerializer()) - context.referenceClass(enumSerializerId) - else null -} - -internal fun IrClass.isEnumWithLegacyGeneratedSerializer(): Boolean = isInternallySerializableEnum() && useGeneratedEnumSerializer - -internal val IrClass.useGeneratedEnumSerializer: Boolean - get() = true // FIXME This would break if we try to use this new IR compiler with pre-1.4.1 serialization versions. I think we just need to raise min runtime version. - -internal val IrClass.isSealedSerializableInterface: Boolean - get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotation() - -internal fun IrClass.isInternallySerializableEnum(): Boolean = - kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs() - fun findStandardKotlinTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? { val typeName = type.classFqName?.toString() val name = when (typeName) { @@ -173,26 +187,47 @@ fun findStandardKotlinTypeSerializer(context: SerializationPluginContext, type: return context.getClassFromRuntimeOrNull(name, SerializationPackages.internalPackageFqName, SerializationPackages.packageFqName) } -fun IrType.isGeneratedSerializableObject(): Boolean { - return classOrNull?.run { owner.kind == ClassKind.OBJECT && owner.hasSerializableOrMetaAnnotationWithoutArgs() } == true +// @Serializable(X::class) -> X +internal fun List.serializableWith(): IrClassSymbol? { + val annotation = findAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return null + val arg = annotation.getValueArgument(0) as? IrClassReference ?: return null + return arg.symbol as? IrClassSymbol } -internal val IrClass.isSerializableObject: Boolean - get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotation() +internal fun getSerializableClassByCompanion(companionClass: IrClass): IrClass? { + if (companionClass.isSerializableObject) return companionClass + if (!companionClass.isCompanion) return null + val classDescriptor = (companionClass.parent as? IrClass) ?: return null + if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null + return classDescriptor +} -// todo: optimize & unify with hasSerializableOrMeta -internal fun IrClass.hasSerializableOrMetaAnnotationWithoutArgs(): Boolean { - val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) - if (annot != null) { - for (i in 0 until annot.valueArgumentsCount) { - if (annot.getValueArgument(i) != null) return false - } - return true +fun BaseIrGenerator.allSealedSerializableSubclassesFor( + irClass: IrClass, + context: SerializationPluginContext +): Pair, List> { + assert(irClass.modality == Modality.SEALED) + fun recursiveSealed(klass: IrClass): Collection { + return klass.sealedSubclasses.map { it.owner }.flatMap { if (it.modality == Modality.SEALED) recursiveSealed(it) else setOf(it) } } - val metaAnnotation = annotations - .flatMap { it.symbol.owner.constructedClass.annotations } - .find { it.isAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) } - return metaAnnotation != null + + val serializableSubtypes = recursiveSealed(irClass).map { it.defaultType } + return serializableSubtypes.mapNotNull { subtype -> + findTypeSerializerOrContextUnchecked(context, subtype)?.let { Pair(subtype, it) } + }.unzip() +} + +internal fun getSerializableClassDescriptorBySerializer(serializer: IrClass): IrClass? { + val serializerForClass = serializer.serializerForClass + if (serializerForClass != null) return serializerForClass.owner + if (serializer.name !in setOf( + SerialEntityNames.SERIALIZER_CLASS_NAME, + SerialEntityNames.GENERATED_SERIALIZER_CLASS + ) + ) return null + val classDescriptor = (serializer.parent as? IrClass) ?: return null + if (!classDescriptor.shouldHaveGeneratedSerializer) return null + return classDescriptor } fun SerializationPluginContext.getClassFromRuntimeOrNull(className: String, vararg packages: FqName): IrClassSymbol? { @@ -214,124 +249,3 @@ fun SerializationPluginContext.getClassFromInternalSerializationPackage(classNam getClassFromRuntimeOrNull(className, SerializationPackages.internalPackageFqName) ?: error("Class $className wasn't found in ${SerializationPackages.internalPackageFqName}. Check that you have correct version of serialization runtime in classpath.") - -internal val IrType.overridenSerializer: IrClassSymbol? - get() { - val desc = this.classOrNull ?: return null - desc.owner.serializableWith?.let { return it } - return null - } - -internal val IrClass.serializableWith: IrClassSymbol? - get() = annotations.serializableWith() - - -// @Serializable(X::class) -> X -internal fun List.serializableWith(): IrClassSymbol? { - val annotation = findAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return null - val arg = annotation.getValueArgument(0) as? IrClassReference ?: return null - return arg.symbol as? IrClassSymbol -} - -internal fun getSerializableClassByCompanion(companionClass: IrClass): IrClass? { - if (companionClass.isSerializableObject) return companionClass - if (!companionClass.isCompanion) return null - val classDescriptor = (companionClass.parent as? IrClass) ?: return null - if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null - return classDescriptor -} - -internal val IrClass.shouldHaveGeneratedMethodsInCompanion: Boolean - get() = this.isSerializableObject || this.isSerializableEnum() || (this.kind == ClassKind.CLASS && hasSerializableOrMetaAnnotation()) || this.isSealedSerializableInterface - -internal fun IrClass.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotation() - -fun IrClass.hasSerializableOrMetaAnnotation() = descriptor.hasSerializableOrMetaAnnotation // TODO - -internal val IrType.genericIndex: Int? - get() = (this.classifierOrNull as? IrTypeParameterSymbol)?.owner?.index - -fun IrType.serialName(): String = this.classOrNull!!.owner.serialName() -fun IrClass.serialName(): String { - return annotations.serialNameValue ?: fqNameWhenAvailable?.asString() ?: error("${this.render()} does not have fqName") -} - -fun AbstractIrGenerator.allSealedSerializableSubclassesFor( - irClass: IrClass, - context: SerializationPluginContext -): Pair, List> { - assert(irClass.modality == Modality.SEALED) - fun recursiveSealed(klass: IrClass): Collection { - return klass.sealedSubclasses.map { it.owner }.flatMap { if (it.modality == Modality.SEALED) recursiveSealed(it) else setOf(it) } - } - - val serializableSubtypes = recursiveSealed(irClass).map { it.defaultType } - return serializableSubtypes.mapNotNull { subtype -> - findTypeSerializerOrContextUnchecked(context, subtype)?.let { Pair(subtype, it) } - }.unzip() -} - -fun IrClass.findEnumValuesMethod() = this.functions.singleOrNull { f -> - f.name == Name.identifier("values") && f.valueParameters.isEmpty() && f.extensionReceiverParameter == null -} ?: throw AssertionError("Enum class does not have single .values() function") - -internal fun IrClass.enumEntries(): List { - check(this.kind == ClassKind.ENUM_CLASS) - return declarations.filterIsInstance().toList() -} - -internal fun IrClass.isEnumWithSerialInfoAnnotation(): Boolean { - if (kind != ClassKind.ENUM_CLASS) return false - if (annotations.hasAnySerialAnnotation) return true - return enumEntries().any { (it.annotations.hasAnySerialAnnotation) } -} - -internal val List.hasAnySerialAnnotation: Boolean - get() = serialNameValue != null || any { it.annotationClass.isSerialInfoAnnotation == true } - -internal val List.serialNameValue: String? - get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)?.getStringConstArgument(0) // @SerialName("foo") - -internal fun getSerializableClassDescriptorBySerializer(serializer: IrClass): IrClass? { - val serializerForClass = serializer.serializerForClass - if (serializerForClass != null) return serializerForClass.owner - if (serializer.name !in setOf( - SerialEntityNames.SERIALIZER_CLASS_NAME, - SerialEntityNames.GENERATED_SERIALIZER_CLASS - ) - ) return null - val classDescriptor = (serializer.parent as? IrClass) ?: return null - if (!classDescriptor.shouldHaveGeneratedSerializer) return null - return classDescriptor -} - -internal fun isKSerializer(type: IrType): Boolean { - val simpleType = type as? IrSimpleType ?: return false - val classifier = simpleType.classifier as? IrClassSymbol ?: return false - val fqName = classifier.owner.fqNameWhenAvailable - return fqName == SerialEntityNames.KSERIALIZER_NAME_FQ || fqName == SerialEntityNames.GENERATED_SERIALIZER_FQ -} - -internal fun IrClass.findPluginGeneratedMethod(name: String): IrSimpleFunction? { - return this.functions.find { - it.name.asString() == name && it.isFromPlugin() - } -} - -internal fun IrDeclaration.isFromPlugin(): Boolean = - this.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) || (this.descriptor as? CallableMemberDescriptor)?.kind == CallableMemberDescriptor.Kind.SYNTHESIZED // old FE doesn't specify origin - -internal fun IrConstructor.isSerializationCtor(): Boolean { - /*kind == CallableMemberDescriptor.Kind.SYNTHESIZED does not work because DeserializedClassConstructorDescriptor loses its kind*/ - return valueParameters.lastOrNull()?.run { - name == SerialEntityNames.dummyParamName && type.classFqName == SerializationPackages.internalPackageFqName.child( - SerialEntityNames.SERIAL_CTOR_MARKER_NAME - ) - } == true -} - -internal fun IrExpression.isInitializePropertyFromParameter(): Boolean = - this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER - -internal val IrConstructorCall.annotationClass - get() = this.symbol.owner.constructedClass \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Synthetics.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Synthetics.kt deleted file mode 100644 index 0f04d462abf..00000000000 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Synthetics.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.jetbrains.kotlinx.serialization.compiler.backend.ir - -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.descriptors.impl.FieldDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.types.KotlinType - - -/** - * Simple property descriptor with backing field without getters/setters for ir generation purposes - */ -class SimpleSyntheticPropertyDescriptor( - owner: ClassDescriptor, - name: String, - type: KotlinType, - isVar: Boolean = false, - visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE -) : PropertyDescriptorImpl( - owner, - null, - Annotations.EMPTY, - Modality.FINAL, - visibility, - isVar, - Name.identifier(name), - CallableMemberDescriptor.Kind.SYNTHESIZED, - owner.source, - false, false, false, false, false, false - ) { - - private val _backingField = FieldDescriptorImpl(Annotations.EMPTY, this) - - init { - super.setType(type, emptyList(), owner.thisAsReceiverParameter, null, emptyList()) - super.initialize(null, null, _backingField, null) - } -} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt index 61451453d7b..9ebd805a3ef 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt @@ -6,15 +6,6 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrProperty -import org.jetbrains.kotlin.ir.declarations.lazy.IrMaybeDeserializedClass -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.isAny -import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtDeclarationWithInitializer import org.jetbrains.kotlin.psi.KtParameter @@ -26,16 +17,11 @@ import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor import org.jetbrains.kotlin.serialization.deserialization.getName -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.isInterface -import org.jetbrains.kotlinx.serialization.compiler.backend.common.isInitializePropertyFromParameter -import org.jetbrains.kotlinx.serialization.compiler.backend.common.isInternalSerializable -import org.jetbrains.kotlinx.serialization.compiler.backend.common.isInternallySerializableEnum import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SERIALIZABLE_PROPERTIES import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginMetadataExtensions -interface ISerializableProperties> { +interface ISerializableProperties { val serializableProperties: List val isExternallySerializable: Boolean val serializableConstructorProperties: List @@ -43,7 +29,7 @@ interface ISerializableProperties> { } class SerializableProperties(private val serializableClass: ClassDescriptor, val bindingContext: BindingContext) : - ISerializableProperties { + ISerializableProperties { private val primaryConstructorParameters: List = serializableClass.unsubstitutedPrimaryConstructor?.valueParameters ?: emptyList() @@ -91,7 +77,7 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val else SerializableProperties(supers, bindingContext).serializableProperties + first + second } - .let { unsort(serializableClass, it) } + .let { restoreCorrectOrderFromClassProtoExtension(serializableClass, it) } isExternallySerializable = serializableClass.isInternallySerializableEnum() || primaryConstructorParameters.size == primaryConstructorProperties.size @@ -134,7 +120,7 @@ fun PropertyDescriptor.declaresDefaultValue(): Boolean { } -internal val ISerializableProperties<*, *, *>.goldenMask: Int +internal val ISerializableProperties<*>.goldenMask: Int get() { var goldenMask = 0 var requiredBit = 1 @@ -147,7 +133,7 @@ internal val ISerializableProperties<*, *, *>.goldenMask: Int return goldenMask } -internal val ISerializableProperties<*, *, *>.goldenMaskList: List +internal val ISerializableProperties<*>.goldenMaskList: List get() { val maskSlotCount = serializableProperties.bitMaskSlotCount() val goldenMaskList = MutableList(maskSlotCount) { 0 } @@ -162,7 +148,7 @@ internal val ISerializableProperties<*, *, *>.goldenMaskList: List return goldenMaskList } -internal fun List>.bitMaskSlotCount() = size / 32 + 1 +internal fun List.bitMaskSlotCount() = size / 32 + 1 internal fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32 internal fun BindingContext.serializablePropertiesFor( @@ -174,81 +160,10 @@ internal fun BindingContext.serializablePropertiesFor( return props } -private fun unsort(descriptor: ClassDescriptor, props: List): List { +internal fun restoreCorrectOrderFromClassProtoExtension(descriptor: ClassDescriptor, props: List

): List

{ if (descriptor !is DeserializedClassDescriptor) return props val correctOrder: List = descriptor.classProto.getExtension(SerializationPluginMetadataExtensions.propertiesNamesInProgramOrder) .map { descriptor.c.nameResolver.getName(it) } - val propsMap = props.associateBy { it.descriptor.name } + val propsMap = props.associateBy { it.originalDescriptorName } return correctOrder.map { propsMap.getValue(it) } -} - -class IrSerializableProperties( - override val serializableProperties: List, - override val isExternallySerializable: Boolean, - override val serializableConstructorProperties: List, - override val serializableStandaloneProperties: List -) : ISerializableProperties { -} - -internal fun serializablePropertiesForIrBackend( - classDescriptor: IrClass, - serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null -): IrSerializableProperties { - val properties = classDescriptor.properties.toList() - val primaryConstructorParams = classDescriptor.primaryConstructor?.valueParameters.orEmpty() - val primaryParamsAsProps = properties.associateBy { it.name }.let { namesMap -> - primaryConstructorParams.mapNotNull { - if (it.name !in namesMap) null else namesMap.getValue(it.name) to it.hasDefaultValue() - }.toMap() - } - - fun isPropSerializable(it: IrProperty) = - if (classDescriptor.isInternalSerializable) !it.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName) - else !DescriptorVisibilities.isPrivate(it.visibility) && ((it.isVar && !it.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName)) || primaryParamsAsProps.contains( - it - )) - - val (primaryCtorSerializableProps, bodySerializableProps) = properties - .asSequence() - .filter { !it.isFakeOverride && !it.isDelegated } - .filter(::isPropSerializable) - .map { - val isConstructorParameterWithDefault = primaryParamsAsProps[it] ?: false - // FIXME: workaround because IrLazyProperty doesn't deserialize information about backing fields. Fallback to descriptor won't work with FIR. - val isPropertyFromAnotherModuleDeclaresDefaultValue = it.descriptor is DeserializedPropertyDescriptor && it.descriptor.declaresDefaultValue() - val isPropertyWithBackingFieldFromAnotherModule = it.descriptor is DeserializedPropertyDescriptor && (it.descriptor.backingField != null || isPropertyFromAnotherModuleDeclaresDefaultValue) - IrSerializableProperty( - it, - isConstructorParameterWithDefault, - it.backingField != null || isPropertyWithBackingFieldFromAnotherModule, - it.backingField?.initializer.let { init -> init != null && !init.expression.isInitializePropertyFromParameter() } || isConstructorParameterWithDefault - || isPropertyFromAnotherModuleDeclaresDefaultValue - ) - } - .filterNot { it.transient } - .partition { primaryParamsAsProps.contains(it.descriptor) } - - val serializableProps = run { - val supers = classDescriptor.getParentClassNotAny() - if (supers == null || !supers.isInternalSerializable) - primaryCtorSerializableProps + bodySerializableProps - else - serializablePropertiesForIrBackend( - supers, - serializationDescriptorSerializer - ).serializableProperties + primaryCtorSerializableProps + bodySerializableProps - } // todo: implement unsorting - - val isExternallySerializable = - classDescriptor.isInternallySerializableEnum() || primaryConstructorParams.size == primaryParamsAsProps.size - - return IrSerializableProperties(serializableProps, isExternallySerializable, primaryCtorSerializableProps, bodySerializableProps) -} - -fun IrClass.getParentClassNotAny(): IrClass? { - val parentClass = - superTypes - .mapNotNull { it.classOrNull?.owner } - .singleOrNull { it.kind == ClassKind.CLASS || it.kind == ClassKind.ENUM_CLASS } ?: return null - return if (parentClass.defaultType.isAny()) null else parentClass } \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt index d51337aa41b..167d8870046 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt @@ -16,58 +16,32 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve -import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.ir.declarations.IrProperty -import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.util.hasAnnotation -import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlinx.serialization.compiler.backend.common.analyzeSpecialSerializers -import org.jetbrains.kotlinx.serialization.compiler.backend.common.genericIndex -import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialNameValue -import org.jetbrains.kotlinx.serialization.compiler.backend.common.serializableWith -import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext -class SerializableProperty( - override val descriptor: PropertyDescriptor, - override val isConstructorParameterWithDefault: Boolean, - hasBackingField: Boolean, - declaresDefaultValue: Boolean -) : ISerializableProperty { - override val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString() - override val type = descriptor.type - override val genericIndex = type.genericIndex - val module = descriptor.module - val serializableWith = descriptor.serializableWith ?: analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType - override val optional = !descriptor.annotations.serialRequired && declaresDefaultValue - override val transient = descriptor.annotations.serialTransient || !hasBackingField - val annotationsWithArguments: List, List>> = - descriptor.annotationsWithArguments() -} - -interface ISerializableProperty { - val descriptor: D +interface ISerializableProperty { val isConstructorParameterWithDefault: Boolean val name: String - val type: T - val genericIndex: Int? + val originalDescriptorName: Name val optional: Boolean val transient: Boolean } -class IrSerializableProperty( - override val descriptor: IrProperty, +class SerializableProperty( + val descriptor: PropertyDescriptor, override val isConstructorParameterWithDefault: Boolean, hasBackingField: Boolean, declaresDefaultValue: Boolean -) : ISerializableProperty { +) : ISerializableProperty { override val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString() - override val type = descriptor.getter!!.returnType as IrSimpleType - override val genericIndex = type.genericIndex - fun serializableWith(ctx: SerializationPluginContext) = descriptor.annotations.serializableWith() ?: analyzeSpecialSerializers(ctx, descriptor.annotations) - override val optional = !descriptor.annotations.hasAnnotation(SerializationAnnotations.requiredAnnotationFqName) && declaresDefaultValue - override val transient = descriptor.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName) || !hasBackingField -} \ No newline at end of file + override val originalDescriptorName: Name = descriptor.name + val type = descriptor.type + val genericIndex = type.genericIndex + val module = descriptor.module + val serializableWith = descriptor.serializableWith ?: analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType + override val optional = !descriptor.annotations.serialRequired && declaresDefaultValue + override val transient = descriptor.annotations.serialTransient || !hasBackingField +} + diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java index f000c8a2141..8371568b598 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java @@ -6,9 +6,10 @@ package org.jetbrains.kotlinx.serialization; import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; import org.jetbrains.kotlin.test.TargetBackend; import org.jetbrains.kotlin.test.TestMetadata; -import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import java.io.File; diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Sealed.ir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Sealed.ir.txt index 077967a028e..9728edc7a9f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Sealed.ir.txt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Sealed.ir.txt @@ -326,7 +326,9 @@ final class Result$Companion$$cachedSerializer$delegate$1 : kotlin/jvm/internal/ DUP LDC (Result.Err) GETSTATIC (Result$Err, INSTANCE, LResult$Err;) - INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, , (Ljava/lang/String;Ljava/lang/Object;)V) + ICONST_0 + ANEWARRAY (java/lang/annotation/Annotation) + INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, , (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/annotation/Annotation;)V) CHECKCAST (kotlinx/serialization/KSerializer) AASTORE ALOAD (1) @@ -335,7 +337,9 @@ final class Result$Companion$$cachedSerializer$delegate$1 : kotlin/jvm/internal/ CHECKCAST (kotlinx/serialization/KSerializer) AASTORE ALOAD (1) - INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, , (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)V) + ICONST_0 + ANEWARRAY (java/lang/annotation/Annotation) + INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, , (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;[Ljava/lang/annotation/Annotation;)V) CHECKCAST (kotlinx/serialization/KSerializer) ARETURN LABEL (L1) @@ -410,7 +414,9 @@ final class Result$Err$$cachedSerializer$delegate$1 : kotlin/jvm/internal/Lambda DUP LDC (Result.Err) GETSTATIC (Result$Err, INSTANCE, LResult$Err;) - INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, , (Ljava/lang/String;Ljava/lang/Object;)V) + ICONST_0 + ANEWARRAY (java/lang/annotation/Annotation) + INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, , (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/annotation/Annotation;)V) CHECKCAST (kotlinx/serialization/KSerializer) ARETURN LABEL (L1) @@ -859,7 +865,9 @@ public final class X$Companion : java/lang/Object { DUP LDC (Result.Err) GETSTATIC (Result$Err, INSTANCE, LResult$Err;) - INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, , (Ljava/lang/String;Ljava/lang/Object;)V) + ICONST_0 + ANEWARRAY (java/lang/annotation/Annotation) + INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, , (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/annotation/Annotation;)V) CHECKCAST (kotlinx/serialization/KSerializer) AASTORE ALOAD (1) @@ -868,7 +876,9 @@ public final class X$Companion : java/lang/Object { CHECKCAST (kotlinx/serialization/KSerializer) AASTORE ALOAD (1) - INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, , (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)V) + ICONST_0 + ANEWARRAY (java/lang/annotation/Annotation) + INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, , (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;[Ljava/lang/annotation/Annotation;)V) CHECKCAST (kotlinx/serialization/KSerializer) ARETURN LABEL (L1)