Refactor most of the new code, split huge GeneratorHelpers.kt appropriately
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 b9fab87..ea5ee96 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.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<IrClassSymbol> {
- 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<IrClassSymbol> by lazy {
- getClassListFromFileAnnotation(
- SerializationAnnotations.contextualFqName,
- ).plus(
- getClassListFromFileAnnotation(
- SerializationAnnotations.contextualOnFileFqName,
- )
- ).toSet()
- }
-
- val additionalSerializersInScopeOfCurrentFile: Map<Pair<IrClassSymbol, Boolean>, 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<KotlinType> {
- 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<KClassValue> = annotation.firstArgument()?.value as? List<KClassValue> ?: return emptyList()
return typeList.map { it.getArgumentType(declarationInFile.module) }
@@ -102,5 +61,6 @@
}
protected fun ClassDescriptor.getFuncDesc(funcName: String): Sequence<FunctionDescriptor> =
- unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) }.asSequence().filterIsInstance<FunctionDescriptor>()
+ unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) }.asSequence()
+ .filterIsInstance<FunctionDescriptor>()
}
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 f0cf7c7..14fa565 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 @@
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 9de53f7..e10fb3e 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.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.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<C, D, T : KotlinTypeMarker, S : ISerializableProperty<D, T>> {
- 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<ClassDescriptor, PropertyDescriptor, KotlinType, SerializableProperty>
-
-class IrSerialTypeInfo(
- override val property: IrSerializableProperty,
- override val elementMethodPrefix: String,
- override val serializer: IrClassSymbol? = null
-) : ISerialTypeInfo<IrClassSymbol, IrProperty, IrSimpleType, IrSerializableProperty>
-
+ 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 @@
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/common/TypeUtilIr.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt
deleted file mode 100644
index a10c7c0..0000000
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * 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.common
-
-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.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.jvm.*
-import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
-import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey
-import org.jetbrains.kotlinx.serialization.compiler.resolve.*
-
-fun AbstractIrGenerator.findTypeSerializerOrContextUnchecked(
- context: SerializationPluginContext, kType: IrType
-): IrClassSymbol? {
- val annotations = kType.annotations
- if (kType.isTypeParameter()) return null
- annotations.serializableWith()?.let { return it }
- additionalSerializersInScopeOfCurrentFile[kType.classOrNull!! to kType.isNullable()]?.let {
- return it
- }
- if (kType.isMarkedNullable()) return findTypeSerializerOrContextUnchecked(context, kType.makeNotNull())
- if (kType.classOrNull in contextualKClassListInCurrentFile) return context.referenceClass(contextSerializerId)
- return analyzeSpecialSerializers(context, annotations) ?: findTypeSerializer(context, kType)
-}
-
-fun analyzeSpecialSerializers(
- context: SerializationPluginContext,
- annotations: List<IrConstructorCall>
-): IrClassSymbol? = when {
- annotations.hasAnnotation(SerializationAnnotations.contextualFqName) || annotations.hasAnnotation(SerializationAnnotations.contextualOnPropertyFqName) ->
- context.referenceClass(contextSerializerId)
- // can be annotation on type usage, e.g. List<@Polymorphic Any>
- annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName) ->
- context.referenceClass(polymorphicSerializerId)
- 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 }
- if (type.isTypeParameter()) return null
- if (type.isArray()) return context.referenceClass(referenceArraySerializerId)
- if (type.isGeneratedSerializableObject()) return context.referenceClass(objectSerializerId)
- val stdSer = findStandardKotlinTypeSerializer(context, type) // see if there is a standard serializer
- ?: findEnumTypeSerializer(context, type)
- if (stdSer != null) return stdSer
- if (type.isInterface() && type.classOrNull?.owner?.isSealedSerializableInterface == false) return context.referenceClass(
- polymorphicSerializerId
- )
- return type.classOrNull?.owner.classSerializer(context) // check for serializer defined on the type
-}
-
-internal fun IrClass?.classSerializer(context: SerializationPluginContext): IrClassSymbol? = this?.let {
- // serializer annotation on class?
- serializableWith?.let { return it }
- // companion object serializer?
- if (hasCompanionObjectAsSerializer) return companionObject()?.symbol
- // can infer @Poly?
- polymorphicSerializerIfApplicableAutomatically(context)?.let { return it }
- // default serializable?
- if (shouldHaveGeneratedSerializer) {
- // $serializer nested class
- return this.declarations
- .filterIsInstance<IrClass>()
- .singleOrNull { it.name == SerialEntityNames.SERIALIZER_CLASS_NAME }?.symbol
- }
- 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
- kind == ClassKind.INTERFACE -> SpecialBuiltins.polymorphicSerializer
- isInternalSerializable && modality == Modality.ABSTRACT -> SpecialBuiltins.polymorphicSerializer
- isInternalSerializable && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer
- else -> null
- }
- return serializer?.let {
- context.getClassFromRuntimeOrNull(
- it,
- SerializationPackages.packageFqName,
- SerializationPackages.internalPackageFqName
- )
- }
-}
-
-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 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) {
- "Z" -> if (type.isBoolean()) "BooleanSerializer" else null
- "B" -> if (type.isByte()) "ByteSerializer" else null
- "S" -> if (type.isShort()) "ShortSerializer" else null
- "I" -> if (type.isInt()) "IntSerializer" else null
- "J" -> if (type.isLong()) "LongSerializer" else null
- "F" -> if (type.isFloat()) "FloatSerializer" else null
- "D" -> if (type.isDouble()) "DoubleSerializer" else null
- "C" -> if (type.isChar()) "CharSerializer" else null
- null -> null
- else -> findStandardKotlinTypeSerializer(typeName)
- } ?: return null
- return context.getClassFromRuntimeOrNull(name, SerializationPackages.internalPackageFqName, SerializationPackages.packageFqName)
-}
-
-fun IrType.isGeneratedSerializableObject(): Boolean {
- return classOrNull?.run { owner.kind == ClassKind.OBJECT && owner.hasSerializableOrMetaAnnotationWithoutArgs() } == true
-}
-
-internal val IrClass.isSerializableObject: Boolean
- get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotation()
-
-// 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
- }
- val metaAnnotation = annotations
- .flatMap { it.symbol.owner.constructedClass.annotations }
- .find { it.isAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) }
- return metaAnnotation != null
-}
-
-fun SerializationPluginContext.getClassFromRuntimeOrNull(className: String, vararg packages: FqName): IrClassSymbol? {
- val listToSearch = if (packages.isEmpty()) SerializationPackages.allPublicPackages else packages.toList()
- for (pkg in listToSearch) {
- referenceClass(ClassId(pkg, Name.identifier(className)))?.let { return it }
- }
- return null
-}
-
-fun SerializationPluginContext.getClassFromRuntime(className: String, vararg packages: FqName): IrClassSymbol {
- return getClassFromRuntimeOrNull(className, *packages) ?: error(
- "Class $className wasn't found in ${packages.toList().ifEmpty { SerializationPackages.allPublicPackages }}. " +
- "Check that you have correct version of serialization runtime in classpath."
- )
-}
-
-fun SerializationPluginContext.getClassFromInternalSerializationPackage(className: String): IrClassSymbol =
- 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<IrConstructorCall>.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<IrSimpleType>, List<IrClassSymbol>> {
- assert(irClass.modality == Modality.SEALED)
- fun recursiveSealed(klass: IrClass): Collection<IrClass> {
- 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<IrEnumEntry> {
- check(this.kind == ClassKind.ENUM_CLASS)
- return declarations.filterIsInstance<IrEnumEntry>().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<IrConstructorCall>.hasAnySerialAnnotation: Boolean
- get() = serialNameValue != null || any { it.annotationClass.isSerialInfoAnnotation == true }
-
-internal val List<IrConstructorCall>.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/BaseIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt
new file mode 100644
index 0000000..0a6c9f1
--- /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<IrClassSymbol> {
+ 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<IrClassSymbol> by lazy {
+ getClassListFromFileAnnotation(
+ SerializationAnnotations.contextualFqName,
+ ).plus(
+ getClassListFromFileAnnotation(
+ SerializationAnnotations.contextualOnFileFqName,
+ )
+ ).toSet()
+ }
+
+ val additionalSerializersInScopeOfCurrentFile: Map<Pair<IrClassSymbol, Boolean>, 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<IrValueDeclaration>,
+ 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<IrSerializableProperty>,
+ 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<IrExpression> = 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<IrExpression>) = 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<IrType>,
+ args: List<IrExpression>
+ ): 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<IrExpression> =
+ // 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<IrExpression>
+ var typeArgs: List<IrType>
+ 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<IrExpression>().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<IrExpression>().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<ClosedRange<Float>>
+ 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 0000000..43c07c6
--- /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<IrSerializableProperty>,
+ 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<IrProperty>()
+ .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<IrValueSymbol, () -> 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<IrValueSymbol, IrExpression?> {
+ if (irClass == null) return emptyMap()
+ val original = irClass.constructors.singleOrNull { it.isPrimary }
+ // default arguments of original constructor
+ val defaultsMap: Map<IrValueSymbol, IrExpression?> =
+ 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/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt
deleted file mode 100644
index 9f6861a..0000000
--- 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 <reified T : IrDeclaration> IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? {
- return declarations.singleOrNull { it.descriptor == descriptor } as? T
- }
-
- fun useFieldMissingOptimization(): Boolean {
- return throwMissedFieldExceptionFunc != null && throwMissedFieldExceptionArrayFunc != null
- }
-
- fun <F: IrFunction> 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("<anonymous>")
- 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<IrType?>,
- valueArguments: List<IrExpression>,
- 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>
- ): 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>
- ): 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 : IrDeclaration> 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<IrSerializableProperty>,
- 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<IrProperty>()
- .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<IrValueDeclaration>,
- 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<IrProperty>(propertyDescriptor)?.getter
- false -> searchForDeclaration<IrProperty>(propertyDescriptor)?.setter
- } ?: run {
- with(descriptor) {
- @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
- property.factory.createFunction(
- fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor),
- name, visibility, modality, returnType!!.toIrType(),
- isInline, isEffectivelyExternal(), isTailrec, isSuspend, isOperator, isInfix, isExpect
- )
- }.also { f ->
- generateOverriddenFunctionSymbols(f, compilerContext.symbolTable)
- f.createParameterDeclarations(descriptor)
- @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
- f.returnType = descriptor.returnType!!.toIrType()
- f.correspondingPropertySymbol = fieldSymbol.owner.correspondingPropertySymbol
- }
- }
-
- irAccessor.body = when (isGetter) {
- true -> generateDefaultGetterBody(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<Int>::class differ from Array<String>::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<IrValueSymbol, IrExpression?> {
- if (irClass == null) return emptyMap()
- val original = irClass.constructors.singleOrNull { it.isPrimary }
- // default arguments of original constructor
- val defaultsMap: Map<IrValueSymbol, IrExpression?> =
- 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<IrValueSymbol, () -> 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<String> {
- assert(enumClass.kind == ClassKind.ENUM_CLASS)
- return enumClass.unsubstitutedMemberScope.getContributedDescriptors().asSequence()
- .filterIsInstance<ClassDescriptor>()
- .filter { it.kind == ClassKind.ENUM_ENTRY }
- .map { it.name.toString() }
- }
-
- fun IrBuilderWithScope.copyAnnotationsFrom(annotations: List<IrConstructorCall>): List<IrExpression> =
- 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<IrExpression>
- var typeArgs: List<IrType>
- 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<IrExpression>().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<IrExpression>().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<ClosedRange<Float>>
- 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<IrConstructorCall> {
- if (!(irClass.isInterface || irClass.descriptor.hasSerializableOrMetaAnnotation)) return emptyList()
- val annotationByFq: MutableMap<FqName, IrConstructorCall> =
- 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<IrType>,
- args: List<IrExpression>
- ): 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<IrExpression> =
- // 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<IrConstructor>().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<IrSerializableProperty>,
- 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<IrExpression> = 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<IrExpression>) = 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 0000000..e39488b
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt
@@ -0,0 +1,582 @@
+/*
+ * 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 <F: IrFunction> 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("<anonymous>")
+ 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<IrType?>,
+ valueArguments: List<IrExpression>,
+ 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>
+ ): 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>
+ ): 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 : IrDeclaration> 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 <reified T : IrDeclaration> 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) {
+ // 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<IrProperty>(propertyDescriptor)?.getter
+ false -> searchForDeclaration<IrProperty>(propertyDescriptor)?.setter
+ } ?: run {
+ with(descriptor) {
+ @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
+ property.factory.createFunction(
+ fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor),
+ name, visibility, modality, returnType!!.toIrType(),
+ isInline, isEffectivelyExternal(), isTailrec, isSuspend, isOperator, isInfix, isExpect
+ )
+ }.also { f ->
+ generateOverriddenFunctionSymbols(f, compilerContext.symbolTable)
+ f.createParameterDeclarations(descriptor)
+ @OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
+ f.returnType = descriptor.returnType!!.toIrType()
+ f.correspondingPropertySymbol = fieldSymbol.owner.correspondingPropertySymbol
+ }
+ }
+
+ irAccessor.body = when (isGetter) {
+ true -> generateDefaultGetterBody(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
+ )
+ }
+
+ fun IrBuilderWithScope.classReference(classSymbol: IrType): IrClassReference =
+ createClassReference(classSymbol, startOffset, endOffset)
+
+ fun collectSerialInfoAnnotations(irClass: IrClass): List<IrConstructorCall> {
+ if (!(irClass.isInterface || irClass.descriptor.hasSerializableOrMetaAnnotation)) return emptyList()
+ val annotationByFq: MutableMap<FqName, IrConstructorCall> =
+ 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<IrConstructorCall>): List<IrExpression> =
+ 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 0000000..de87f71
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt
@@ -0,0 +1,204 @@
+/*
+ * 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
+import org.jetbrains.kotlinx.serialization.compiler.resolve.hasSerializableOrMetaAnnotation
+
+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()
+
+// 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
+ }
+ val metaAnnotation = annotations
+ .flatMap { it.symbol.owner.constructedClass.annotations }
+ .find { it.isAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) }
+ return metaAnnotation != null
+}
+
+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()
+
+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 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<IrEnumEntry> {
+ check(this.kind == ClassKind.ENUM_CLASS)
+ return declarations.filterIsInstance<IrEnumEntry>().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? { // todo: remove nullability W/A when FIR plugin will add proper ctor
+ return declarations.filterIsInstance<IrConstructor>().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<IrConstructorCall>.hasAnySerialAnnotation: Boolean
+ get() = serialNameValue != null || any { it.annotationClass.isSerialInfoAnnotation == true }
+
+internal val List<IrConstructorCall>.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 0000000..1aff0d2
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrSerializableProperties.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.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.ISerializableProperties
+import org.jetbrains.kotlinx.serialization.compiler.resolve.ISerializableProperty
+import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
+import org.jetbrains.kotlinx.serialization.compiler.resolve.declaresDefaultValue
+
+class IrSerializableProperty(
+ val ir: IrProperty,
+ override val isConstructorParameterWithDefault: Boolean,
+ hasBackingField: Boolean,
+ declaresDefaultValue: Boolean
+) : ISerializableProperty<IrSimpleType> {
+ override val name = ir.annotations.serialNameValue ?: ir.name.asString()
+ override val type = ir.getter!!.returnType as IrSimpleType
+ override 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<IrSerializableProperty>,
+ override val isExternallySerializable: Boolean,
+ override val serializableConstructorProperties: List<IrSerializableProperty>,
+ override val serializableStandaloneProperties: List<IrSerializableProperty>
+) : ISerializableProperties<IrSimpleType, IrSerializableProperty>
+
+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.ir) }
+
+ val serializableProps = run {
+ val supers = classDescriptor.getSuperClassNotAny()
+ 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)
+}
\ 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 b5b61c9..ac81e44 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 @@
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 5b57523..1614cec 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.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<IrSimpleFunction> {
- (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,8 +59,8 @@
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
@@ -110,7 +105,7 @@
irSerializableClass.annotations += annotationCtorCall
}
- fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) {
+ fun generateLazySerializerGetter(methodDescriptor: IrSimpleFunction) {
val serializer = requireNotNull(
findTypeSerializer(
compilerContext,
@@ -124,21 +119,20 @@
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 +141,6 @@
)
val args: List<IrExpression> = getter.valueParameters.map { irGet(it) }
val expr = serializerInstance(
- this@SerializableCompanionIrGenerator,
serializer, compilerContext,
serializableIrClass.defaultType
) { it, _ -> args[it] }
@@ -157,25 +150,25 @@
generateSerializerFactoryIfNeeded(methodDescriptor)
}
- private fun generateSerializerFactoryIfNeeded(getterDescriptor: FunctionDescriptor) {
+ private fun generateSerializerFactoryIfNeeded(getterDescriptor: IrSimpleFunction) {
if (!irClass.descriptor.needSerializerFactory()) return
val serialFactoryDescriptor = irClass.findDeclaration<IrSimpleFunction> {
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<IrSimpleFunction>()
.single { it.name.asString() == "get" }
val serializers: List<IrExpression> = (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 b974b20..959e212 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.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 @@
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 @@
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 @@
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 @@
+irIfThenElse(compilerContext.irBuiltIns.unitType, propNotSeenTest, ifNotSeenExpr, assignParamExpr)
- statementsAfterSerializableProperty[prop.descriptor]?.forEach { +it }
+ statementsAfterSerializableProperty[prop.ir]?.forEach { +it }
}
// Handle function-intialized interface delegates
@@ -271,7 +270,7 @@
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 @@
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 @@
}
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 5110370..687bfd4 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 @@
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.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 a94c333..9327fc1 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.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 @@
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 @@
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 75c4587..706be67 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
@@ -37,7 +37,6 @@
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 +54,10 @@
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 +90,7 @@
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() }
}
}
@@ -184,7 +183,7 @@
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,
@@ -305,8 +304,8 @@
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 +325,6 @@
whenDoNot: (sti: IrSerialTypeInfo) -> FunctionWithArgs,
returnTypeHint: IrType? = null
): IrExpression = formEncodeDecodePropertyCall(
- this@SerializerIrGenerator,
encoder,
property,
whenHaveSerializer,
@@ -385,7 +383,7 @@
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<IrProperty>()
@@ -395,7 +393,7 @@
// 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 +423,7 @@
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 +433,7 @@
}, 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 +490,9 @@
)
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<IrExpression> = serializableProperties.map { serialPropertiesMap.getValue(it.descriptor).get() }
+ var args: List<IrExpression> = serializableProperties.map { serialPropertiesMap.getValue(it.ir).get() }
args = bitMasks.map { irGet(it) } + args + irNull()
+irReturn(irInvoke(null, deserCtor, typeArgs, args))
} else {
@@ -574,9 +572,9 @@
bitMasks: List<IrVariable>
) {
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 +585,12 @@
)
)
- 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)
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt
new file mode 100644
index 0000000..048f837
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt
@@ -0,0 +1,251 @@
+/*
+ * 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.ClassKind
+import org.jetbrains.kotlin.descriptors.Modality
+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.symbols.IrClassSymbol
+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.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
+
+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
+ if (kType.isTypeParameter()) return null
+ annotations.serializableWith()?.let { return it }
+ additionalSerializersInScopeOfCurrentFile[kType.classOrNull!! to kType.isNullable()]?.let {
+ return it
+ }
+ if (kType.isMarkedNullable()) return findTypeSerializerOrContextUnchecked(context, kType.makeNotNull())
+ if (kType.classOrNull in contextualKClassListInCurrentFile) return context.referenceClass(contextSerializerId)
+ return analyzeSpecialSerializers(context, annotations) ?: findTypeSerializer(context, kType)
+}
+
+fun analyzeSpecialSerializers(
+ context: SerializationPluginContext,
+ annotations: List<IrConstructorCall>
+): IrClassSymbol? = when {
+ annotations.hasAnnotation(SerializationAnnotations.contextualFqName) || annotations.hasAnnotation(SerializationAnnotations.contextualOnPropertyFqName) ->
+ context.referenceClass(contextSerializerId)
+ // can be annotation on type usage, e.g. List<@Polymorphic Any>
+ annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName) ->
+ context.referenceClass(polymorphicSerializerId)
+ else -> null
+}
+
+
+fun findTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? {
+ type.overridenSerializer?.let { return it }
+ if (type.isTypeParameter()) return null
+ if (type.isArray()) return context.referenceClass(referenceArraySerializerId)
+ if (type.isGeneratedSerializableObject()) return context.referenceClass(objectSerializerId)
+ val stdSer = findStandardKotlinTypeSerializer(context, type) // see if there is a standard serializer
+ ?: findEnumTypeSerializer(context, type)
+ if (stdSer != null) return stdSer
+ if (type.isInterface() && type.classOrNull?.owner?.isSealedSerializableInterface == false) return context.referenceClass(
+ polymorphicSerializerId
+ )
+ 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?
+ serializableWith?.let { return it }
+ // companion object serializer?
+ if (hasCompanionObjectAsSerializer) return companionObject()?.symbol
+ // can infer @Poly?
+ polymorphicSerializerIfApplicableAutomatically(context)?.let { return it }
+ // default serializable?
+ if (shouldHaveGeneratedSerializer) {
+ // $serializer nested class
+ return this.declarations
+ .filterIsInstance<IrClass>()
+ .singleOrNull { it.name == SerialEntityNames.SERIALIZER_CLASS_NAME }?.symbol
+ }
+ return null
+}
+
+internal fun IrClass.polymorphicSerializerIfApplicableAutomatically(context: SerializationPluginContext): IrClassSymbol? {
+ val serializer = when {
+ kind == ClassKind.INTERFACE && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer
+ kind == ClassKind.INTERFACE -> SpecialBuiltins.polymorphicSerializer
+ isInternalSerializable && modality == Modality.ABSTRACT -> SpecialBuiltins.polymorphicSerializer
+ isInternalSerializable && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer
+ else -> null
+ }
+ return serializer?.let {
+ context.getClassFromRuntimeOrNull(
+ it,
+ SerializationPackages.packageFqName,
+ SerializationPackages.internalPackageFqName
+ )
+ }
+}
+
+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()
+
+internal val IrClass.serializerForClass: IrClassSymbol?
+ get() = (annotations.findAnnotation(SerializationAnnotations.serializerAnnotationFqName)
+ ?.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol
+
+fun findStandardKotlinTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? {
+ val typeName = type.classFqName?.toString()
+ val name = when (typeName) {
+ "Z" -> if (type.isBoolean()) "BooleanSerializer" else null
+ "B" -> if (type.isByte()) "ByteSerializer" else null
+ "S" -> if (type.isShort()) "ShortSerializer" else null
+ "I" -> if (type.isInt()) "IntSerializer" else null
+ "J" -> if (type.isLong()) "LongSerializer" else null
+ "F" -> if (type.isFloat()) "FloatSerializer" else null
+ "D" -> if (type.isDouble()) "DoubleSerializer" else null
+ "C" -> if (type.isChar()) "CharSerializer" else null
+ null -> null
+ else -> findStandardKotlinTypeSerializer(typeName)
+ } ?: return null
+ return context.getClassFromRuntimeOrNull(name, SerializationPackages.internalPackageFqName, SerializationPackages.packageFqName)
+}
+
+// @Serializable(X::class) -> X
+internal fun List<IrConstructorCall>.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
+}
+
+fun BaseIrGenerator.allSealedSerializableSubclassesFor(
+ irClass: IrClass,
+ context: SerializationPluginContext
+): Pair<List<IrSimpleType>, List<IrClassSymbol>> {
+ assert(irClass.modality == Modality.SEALED)
+ fun recursiveSealed(klass: IrClass): Collection<IrClass> {
+ 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()
+}
+
+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? {
+ val listToSearch = if (packages.isEmpty()) SerializationPackages.allPublicPackages else packages.toList()
+ for (pkg in listToSearch) {
+ referenceClass(ClassId(pkg, Name.identifier(className)))?.let { return it }
+ }
+ return null
+}
+
+fun SerializationPluginContext.getClassFromRuntime(className: String, vararg packages: FqName): IrClassSymbol {
+ return getClassFromRuntimeOrNull(className, *packages) ?: error(
+ "Class $className wasn't found in ${packages.toList().ifEmpty { SerializationPackages.allPublicPackages }}. " +
+ "Check that you have correct version of serialization runtime in classpath."
+ )
+}
+
+fun SerializationPluginContext.getClassFromInternalSerializationPackage(className: String): IrClassSymbol =
+ getClassFromRuntimeOrNull(className, SerializationPackages.internalPackageFqName)
+ ?: error("Class $className wasn't found in ${SerializationPackages.internalPackageFqName}. Check that you have correct version of serialization runtime in classpath.")
+
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 0f04d46..0000000
--- 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 6145145..1d55337 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
@@ -27,15 +18,11 @@
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<D, T, S : ISerializableProperty<D, T>> {
+interface ISerializableProperties<T, S : ISerializableProperty<T>> {
val serializableProperties: List<S>
val isExternallySerializable: Boolean
val serializableConstructorProperties: List<S>
@@ -43,7 +30,7 @@
}
class SerializableProperties(private val serializableClass: ClassDescriptor, val bindingContext: BindingContext) :
- ISerializableProperties<PropertyDescriptor, KotlinType, SerializableProperty> {
+ ISerializableProperties<KotlinType, SerializableProperty> {
private val primaryConstructorParameters: List<ValueParameterDescriptor> =
serializableClass.unsubstitutedPrimaryConstructor?.valueParameters ?: emptyList()
@@ -134,7 +121,7 @@
}
-internal val ISerializableProperties<*, *, *>.goldenMask: Int
+internal val ISerializableProperties<*, *>.goldenMask: Int
get() {
var goldenMask = 0
var requiredBit = 1
@@ -147,7 +134,7 @@
return goldenMask
}
-internal val ISerializableProperties<*, *, *>.goldenMaskList: List<Int>
+internal val ISerializableProperties<*, *>.goldenMaskList: List<Int>
get() {
val maskSlotCount = serializableProperties.bitMaskSlotCount()
val goldenMaskList = MutableList(maskSlotCount) { 0 }
@@ -162,7 +149,7 @@
return goldenMaskList
}
-internal fun List<ISerializableProperty<*, *>>.bitMaskSlotCount() = size / 32 + 1
+internal fun List<ISerializableProperty<*>>.bitMaskSlotCount() = size / 32 + 1
internal fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32
internal fun BindingContext.serializablePropertiesFor(
@@ -180,75 +167,4 @@
.map { descriptor.c.nameResolver.getName(it) }
val propsMap = props.associateBy { it.descriptor.name }
return correctOrder.map { propsMap.getValue(it) }
-}
-
-class IrSerializableProperties(
- override val serializableProperties: List<IrSerializableProperty>,
- override val isExternallySerializable: Boolean,
- override val serializableConstructorProperties: List<IrSerializableProperty>,
- override val serializableStandaloneProperties: List<IrSerializableProperty>
-) : ISerializableProperties<IrProperty, IrSimpleType, IrSerializableProperty> {
-}
-
-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 d51337a..a06e5e4 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,40 +16,12 @@
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.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<PropertyDescriptor, KotlinType> {
- 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<Triple<ClassDescriptor, List<ValueArgument>, List<ValueParameterDescriptor>>> =
- descriptor.annotationsWithArguments()
-}
-
-interface ISerializableProperty<D, T> {
- val descriptor: D
+interface ISerializableProperty<T> {
val isConstructorParameterWithDefault: Boolean
val name: String
val type: T
@@ -58,16 +30,18 @@
val transient: Boolean
}
-class IrSerializableProperty(
- override val descriptor: IrProperty,
+class SerializableProperty(
+ val descriptor: PropertyDescriptor,
override val isConstructorParameterWithDefault: Boolean,
hasBackingField: Boolean,
declaresDefaultValue: Boolean
-) : ISerializableProperty<IrProperty, IrSimpleType> {
+) : ISerializableProperty<KotlinType> {
override val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString()
- override val type = descriptor.getter!!.returnType as IrSimpleType
+ override val type = descriptor.type
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
+ 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/testData/codegen/Sealed.ir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Sealed.ir.txt
index 077967a..9728edc 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 @@
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
- INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;)V)
+ ICONST_0
+ ANEWARRAY (java/lang/annotation/Annotation)
+ INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/annotation/Annotation;)V)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ALOAD (1)
@@ -335,7 +337,9 @@
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ALOAD (1)
- INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)V)
+ ICONST_0
+ ANEWARRAY (java/lang/annotation/Annotation)
+ INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (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 @@
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
- INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;)V)
+ ICONST_0
+ ANEWARRAY (java/lang/annotation/Annotation)
+ INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/annotation/Annotation;)V)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
@@ -859,7 +865,9 @@
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
- INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;)V)
+ ICONST_0
+ ANEWARRAY (java/lang/annotation/Annotation)
+ INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/annotation/Annotation;)V)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ALOAD (1)
@@ -868,7 +876,9 @@
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ALOAD (1)
- INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)V)
+ ICONST_0
+ ANEWARRAY (java/lang/annotation/Annotation)
+ INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;[Ljava/lang/annotation/Annotation;)V)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)