From b0716fe725dbf3a3d828c81ebb58caa1cf89185f Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Mon, 25 Jul 2022 19:36:47 +0200 Subject: [PATCH] Adapt IR serialization plugin so it works both with FIR and old FE (except some places that are not currently supported by FIR part of plugin) --- .../compiler/backend/TransformerUtil.kt | 5 +- .../backend/common/AbstractSerialGenerator.kt | 41 ++++++ .../compiler/backend/common/TypeUtil.kt | 24 +++- .../compiler/backend/common/TypeUtilIr.kt | 122 +++++++++--------- .../compiler/backend/ir/GeneratorHelpers.kt | 50 +++---- .../ir/SerializableCompanionIrGenerator.kt | 45 ++++--- .../backend/ir/SerializableIrGenerator.kt | 19 +-- .../backend/ir/SerializerIrGenerator.kt | 29 +++-- .../SerializationLoweringExtension.kt | 21 ++- .../SerializationResolveExtension.kt | 11 +- .../resolve/KSerializerDescriptorResolver.kt | 59 +++++---- .../resolve/SerializableProperties.kt | 103 ++++++++------- .../compiler/resolve/SerializableProperty.kt | 16 +-- .../SerializationIrBoxTestGenerated.java | 15 ++- .../testData/boxIr/annotationsOnFile.kt | 78 +++++++++++ .../testData/boxIr/generics.kt | 22 ++++ 16 files changed, 436 insertions(+), 224 deletions(-) create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/annotationsOnFile.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/generics.kt diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/TransformerUtil.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/TransformerUtil.kt index 87f39dbedf2..79582dd0f25 100644 --- a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/TransformerUtil.kt +++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/TransformerUtil.kt @@ -264,6 +264,7 @@ internal fun IrCall.getCorrespondingProperty(): IrProperty = symbol.owner.correspondingPropertySymbol?.owner ?: error("Atomic property accessor ${this.render()} expected to have non-null correspondingPropertySymbol") +@OptIn(FirIncompatiblePluginAPI::class) internal fun IrPluginContext.referencePackageFunction( packageName: String, name: String, @@ -274,6 +275,7 @@ internal fun IrPluginContext.referencePackageFunction( error("Exception while looking for the function `$name` in package `$packageName`: ${e.message}") } +@OptIn(FirIncompatiblePluginAPI::class) internal fun IrPluginContext.referenceFunction(classSymbol: IrClassSymbol, functionName: String): IrSimpleFunctionSymbol { val functionId = FqName("$KOTLIN.${classSymbol.owner.name}.$functionName") return try { @@ -283,18 +285,19 @@ internal fun IrPluginContext.referenceFunction(classSymbol: IrClassSymbol, funct } } +@OptIn(FirIncompatiblePluginAPI::class) private fun IrPluginContext.referenceArrayClass(irType: IrSimpleType): IrClassSymbol { val jsArrayName = irType.getArrayClassFqName() return referenceClass(jsArrayName) ?: error("Array class $jsArrayName was not found in the context") } +@OptIn(FirIncompatiblePluginAPI::class) internal fun IrPluginContext.getArrayConstructorSymbol( irType: IrSimpleType, predicate: (IrConstructorSymbol) -> Boolean = { true } ): IrConstructorSymbol { val jsArrayName = irType.getArrayClassFqName() return try { - @Suppress("DEPRECATION") // caution: referenceConstructors(fqName) doesn't work with FIR referenceConstructors(jsArrayName).single(predicate) } catch (e: RuntimeException) { error("Array constructor $jsArrayName matching the predicate was not found in the context") 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 aa986639bb2..b9fab87cca3 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,9 +5,19 @@ 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 @@ -21,6 +31,37 @@ import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations import org.jetbrains.kotlinx.serialization.compiler.resolve.isKSerializer import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor +abstract class AbstractIrGenerator(private val currentClass: IrClass) { + private fun getClassListFromFileAnnotation(annotationFqName: FqName): List { + val annotation = currentClass.fileParent.annotations.findAnnotation(annotationFqName) ?: return emptyList() + val vararg = annotation.getValueArgument(0) as? IrVararg ?: return emptyList() + return vararg.elements + .mapNotNull { (it as? IrClassReference)?.symbol as? IrClassSymbol} + } + + val contextualKClassListInCurrentFile: Set by lazy { + getClassListFromFileAnnotation( + SerializationAnnotations.contextualFqName, + ).plus( + getClassListFromFileAnnotation( + SerializationAnnotations.contextualOnFileFqName, + ) + ).toSet() + } + + val additionalSerializersInScopeOfCurrentFile: Map, IrClassSymbol> by lazy { + getClassListFromFileAnnotation(SerializationAnnotations.additionalSerializersFqName,) + .associateBy( + { serializerSymbol -> + val kotlinType = (serializerSymbol.owner.superTypes.find(::isKSerializer) as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull + val classSymbol = kotlinType?.classOrNull + ?: throw AssertionError("Argument for ${SerializationAnnotations.additionalSerializersFqName} does not implement KSerializer or does not provide serializer for concrete type") + classSymbol to kotlinType.isNullable() + }, + { it } + ) + } +} abstract class AbstractSerialGenerator(val bindingContext: BindingContext?, val currentDeclaration: ClassDescriptor) { 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 c45f02ccb9b..6bd16a0aa70 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 @@ -6,7 +6,6 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.common import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.descriptors.* @@ -14,6 +13,7 @@ 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 @@ -63,17 +63,29 @@ fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module return null } +fun AbstractIrGenerator.findAddOnSerializer(propertyType: IrType, ctx: SerializationPluginContext): IrClassSymbol? { + val classSymbol = propertyType.classOrNull ?: return null + additionalSerializersInScopeOfCurrentFile[classSymbol to propertyType.isNullable()]?.let { return it } + if (classSymbol in contextualKClassListInCurrentFile) + return ctx.getClassFromRuntime(SpecialBuiltins.contextSerializer) + if (classSymbol.owner.annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName)) + return ctx.getClassFromRuntime(SpecialBuiltins.polymorphicSerializer) + if (propertyType.isNullable()) return findAddOnSerializer(propertyType.makeNotNull(), ctx) + return null +} + + fun KotlinType.isGeneratedSerializableObject() = toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs } == true -fun AbstractSerialGenerator.getIrSerialTypeInfo(property: IrSerializableProperty, ctx: SerializationPluginContext): IrSerialTypeInfo { +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?.let { return SerializableInfo(it.classOrNull!!) } -// TODO findAddOnSerializer(T, property.module)?.let { return SerializableInfo(it) } - T.overridenSerializer?.let { return SerializableInfo(it.classOrNull!!) } + 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( @@ -82,7 +94,7 @@ fun AbstractSerialGenerator.getIrSerialTypeInfo(property: IrSerializablePropert ) T.isString() -> IrSerialTypeInfo(property, "String") T.isArray() -> { - val serializer = property.serializableWith?.classOrNull ?: ctx.getClassFromRuntime(SpecialBuiltins.referenceArraySerializer) + val serializer = property.serializableWith(ctx) ?: ctx.getClassFromInternalSerializationPackage(SpecialBuiltins.referenceArraySerializer) SerializableInfo(serializer) } else -> { 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 index 9e7a1376d5f..a10c7c09717 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt @@ -5,10 +5,12 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.common -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.codegen.CompilationException -import org.jetbrains.kotlin.descriptors.* +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 @@ -20,30 +22,22 @@ 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.kotlin.resolve.descriptorUtil.classId -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId -import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId -import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.objectSerializerId -import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId -import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId +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 AbstractSerialGenerator.findTypeSerializerOrContextUnchecked( +fun AbstractIrGenerator.findTypeSerializerOrContextUnchecked( context: SerializationPluginContext, kType: IrType ): IrClassSymbol? { val annotations = kType.annotations if (kType.isTypeParameter()) return null - annotations.serializableWith()?.let { return it.classOrNull } - additionalSerializersInScopeOfCurrentFile[kType.classOrNull?.descriptor to kType.isMarkedNullable()]?.let { - return context.referenceClass( - ClassId.topLevel(it.fqNameSafe) - ) + annotations.serializableWith()?.let { return it } + additionalSerializersInScopeOfCurrentFile[kType.classOrNull!! to kType.isNullable()]?.let { + return it } if (kType.isMarkedNullable()) return findTypeSerializerOrContextUnchecked(context, kType.makeNotNull()) - if (kType.toKotlinType() in contextualKClassListInCurrentFile) return context.referenceClass(contextSerializerId) + if (kType.classOrNull in contextualKClassListInCurrentFile) return context.referenceClass(contextSerializerId) return analyzeSpecialSerializers(context, annotations) ?: findTypeSerializer(context, kType) } @@ -59,22 +53,15 @@ fun analyzeSpecialSerializers( else -> null } -fun AbstractSerialGenerator.findTypeSerializerOrContext( - context: SerializationPluginContext, kType: IrType, - sourceElement: PsiElement? = null +fun AbstractIrGenerator.findTypeSerializerOrContext( + context: SerializationPluginContext, kType: IrType ): IrClassSymbol? { if (kType.isTypeParameter()) return null - return findTypeSerializerOrContextUnchecked(context, kType) ?: throw CompilationException( - "Serializer for element of type $kType has not been found.\n" + - "To use context serializer as fallback, explicitly annotate element with @Contextual", - null, - sourceElement - ) + return findTypeSerializerOrContextUnchecked(context, kType) ?: error("Serializer for element of type ${kType.render()} has not been found") } fun findTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? { - val userOverride = type.overridenSerializer - if (userOverride != null) return userOverride.classOrNull + type.overridenSerializer?.let { return it } if (type.isTypeParameter()) return null if (type.isArray()) return context.referenceClass(referenceArraySerializerId) if (type.isGeneratedSerializableObject()) return context.referenceClass(objectSerializerId) @@ -89,7 +76,7 @@ fun findTypeSerializer(context: SerializationPluginContext, type: IrType): IrCla internal fun IrClass?.classSerializer(context: SerializationPluginContext): IrClassSymbol? = this?.let { // serializer annotation on class? - serializableWith?.let { return it.classOrNull } + serializableWith?.let { return it } // companion object serializer? if (hasCompanionObjectAsSerializer) return companionObject()?.symbol // can infer @Poly? @@ -104,6 +91,11 @@ internal fun IrClass?.classSerializer(context: SerializationPluginContext): IrCl return null } +private val IrClass.isSerialInfoAnnotation: Boolean + get() = annotations.hasAnnotation(SerializationAnnotations.serialInfoFqName) + || annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName) + || annotations.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) + internal val IrClass.shouldHaveGeneratedSerializer: Boolean get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isEnumWithLegacyGeneratedSerializer() @@ -116,7 +108,13 @@ internal fun IrClass.polymorphicSerializerIfApplicableAutomatically(context: Ser isInternalSerializable && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer else -> null } - return serializer?.let { context.getClassFromRuntimeOrNull(it, SerializationPackages.packageFqName, SerializationPackages.internalPackageFqName) } + return serializer?.let { + context.getClassFromRuntimeOrNull( + it, + SerializationPackages.packageFqName, + SerializationPackages.internalPackageFqName + ) + } } internal val IrClass.isInternalSerializable: Boolean @@ -131,13 +129,14 @@ internal val IrClass.isStaticSerializable: Boolean get() = this.typeParameters.i internal val IrClass.hasCompanionObjectAsSerializer: Boolean - get() = isInternallySerializableObject || companionObject()?.serializerForClass == this.defaultType + get() = isInternallySerializableObject || companionObject()?.serializerForClass == this.symbol internal val IrClass.isInternallySerializableObject: Boolean get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs() -internal val IrClass.serializerForClass: IrType? - get() = null // TODO("Serializer(forClass)") +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 @@ -149,10 +148,10 @@ fun findEnumTypeSerializer(context: SerializationPluginContext, type: IrType): I internal fun IrClass.isEnumWithLegacyGeneratedSerializer(): Boolean = isInternallySerializableEnum() && useGeneratedEnumSerializer internal val IrClass.useGeneratedEnumSerializer: Boolean - get() = true // todo ???? + 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 && hasSerializableOrMetaAnnotationWithoutArgs() // in previous version, it was just 'serializableOrMeta' + get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotation() internal fun IrClass.isInternallySerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs() @@ -181,7 +180,7 @@ fun IrType.isGeneratedSerializableObject(): Boolean { internal val IrClass.isSerializableObject: Boolean get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotation() -// todo: optimize & unify +// todo: optimize & unify with hasSerializableOrMeta internal fun IrClass.hasSerializableOrMetaAnnotationWithoutArgs(): Boolean { val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) if (annot != null) { @@ -205,9 +204,10 @@ fun SerializationPluginContext.getClassFromRuntimeOrNull(className: String, vara } 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.") + 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 = @@ -215,23 +215,22 @@ fun SerializationPluginContext.getClassFromInternalSerializationPackage(classNam ?: error("Class $className wasn't found in ${SerializationPackages.internalPackageFqName}. Check that you have correct version of serialization runtime in classpath.") -internal val IrType.overridenSerializer: IrSimpleType? +internal val IrType.overridenSerializer: IrClassSymbol? get() { val desc = this.classOrNull ?: return null desc.owner.serializableWith?.let { return it } return null } -internal val IrClass.serializableWith: IrSimpleType? +internal val IrClass.serializableWith: IrClassSymbol? get() = annotations.serializableWith() -internal fun List.serializableWith(): IrSimpleType? { - // XXX::class -// val annotationArg = -// firstOrNull { it.type.classFqName == SerializationAnnotations.serializerAnnotationFqName }?.getValueArgument(0) ?: return null - // TODO("SerializableWith") - return null +// @Serializable(X::class) -> X +internal fun List.serializableWith(): IrClassSymbol? { + val annotation = findAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return null + val arg = annotation.getValueArgument(0) as? IrClassReference ?: return null + return arg.symbol as? IrClassSymbol } internal fun getSerializableClassByCompanion(companionClass: IrClass): IrClass? { @@ -252,12 +251,12 @@ fun IrClass.hasSerializableOrMetaAnnotation() = descriptor.hasSerializableOrMeta internal val IrType.genericIndex: Int? get() = (this.classifierOrNull as? IrTypeParameterSymbol)?.owner?.index -fun IrType.serialName(): String = this.classOrNull!!.owner.serialName() +fun IrType.serialName(): String = this.classOrNull!!.owner.serialName() fun IrClass.serialName(): String { - return descriptor.serialName() // TODO + return annotations.serialNameValue ?: fqNameWhenAvailable?.asString() ?: error("${this.render()} does not have fqName") } -fun AbstractSerialGenerator.allSealedSerializableSubclassesFor( +fun AbstractIrGenerator.allSealedSerializableSubclassesFor( irClass: IrClass, context: SerializationPluginContext ): Pair, List> { @@ -278,9 +277,7 @@ fun IrClass.findEnumValuesMethod() = this.functions.singleOrNull { f -> internal fun IrClass.enumEntries(): List { check(this.kind == ClassKind.ENUM_CLASS) - return declarations.filterIsInstance() -// .filter { it.kind == ClassKind.ENUM_ENTRY } - .toList() + return declarations.filterIsInstance().toList() } internal fun IrClass.isEnumWithSerialInfoAnnotation(): Boolean { @@ -290,14 +287,14 @@ internal fun IrClass.isEnumWithSerialInfoAnnotation(): Boolean { } internal val List.hasAnySerialAnnotation: Boolean - get() = false // TODO serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true } + get() = serialNameValue != null || any { it.annotationClass.isSerialInfoAnnotation == true } internal val List.serialNameValue: String? - get() = null // todo("List.serialNameValue") @SerialName + get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)?.getStringConstArgument(0) // @SerialName("foo") internal fun getSerializableClassDescriptorBySerializer(serializer: IrClass): IrClass? { val serializerForClass = serializer.serializerForClass - if (serializerForClass != null) return serializerForClass.classOrNull?.owner + if (serializerForClass != null) return serializerForClass.owner if (serializer.name !in setOf( SerialEntityNames.SERIALIZER_CLASS_NAME, SerialEntityNames.GENERATED_SERIALIZER_CLASS @@ -316,9 +313,14 @@ internal fun isKSerializer(type: IrType): Boolean { } internal fun IrClass.findPluginGeneratedMethod(name: String): IrSimpleFunction? { - return this.functions.find { it.name.asString() == name && it.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) } + 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 { @@ -328,4 +330,8 @@ internal fun IrConstructor.isSerializationCtor(): Boolean { } == true } -internal fun IrExpression.isInitializePropertyFromParameter(): Boolean = this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER \ No newline at end of file +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/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt index a4798f31b9e..6f4a792c683 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt @@ -7,15 +7,16 @@ 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.ir.deepCopyWithVariables import org.jetbrains.kotlin.backend.common.lower.irIfThen -import org.jetbrains.kotlin.backend.common.sourceElement 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.* @@ -25,19 +26,20 @@ 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.backend.jvm.ir.representativeUpperBound -import org.jetbrains.kotlin.builtins.StandardNames 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.psi -import org.jetbrains.kotlin.resolve.descriptorUtil.* +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.resolve.source.getPsi -import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.typeUtil.* +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.* @@ -71,9 +73,12 @@ interface IrBuilderExtension { } fun addFunctionBody(function: F, bodyGen: IrBlockBodyBuilder.(F) -> Unit) { - function.body = DeclarationIrBuilder(compilerContext, function.symbol, function.startOffset, function.endOffset).irBlockBody( - function.startOffset, - function.endOffset + 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) } } @@ -136,7 +141,7 @@ interface IrBuilderExtension { ): 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.descriptor.valueParameters.size == 2 && it.descriptor.valueParameters[0].type == lazySafeModeClassDescriptor.defaultType + it.owner.valueParameters.size == 2 && it.owner.valueParameters[0].type == lazySafeModeClassDescriptor.defaultType } val publicationEntryDescriptor = lazySafeModeClassDescriptor.enumEntries().single { it.name == LAZY_PUBLICATION_MODE_NAME } @@ -814,7 +819,7 @@ interface IrBuilderExtension { ): IrExpression? { val nullableSerClass = compilerContext.referenceProperties(SerialEntityNames.wrapIntoNullableCallableId).single() val serializer = - property.serializableWith?.classOrNull + property.serializableWith(compilerContext) ?: if (!property.type.isTypeParameter()) generator.findTypeSerializerOrContext( compilerContext, property.type @@ -883,7 +888,7 @@ interface IrBuilderExtension { } fun IrBuilderWithScope.serializerInstance( - enclosingGenerator: AbstractSerialGenerator, + enclosingGenerator: AbstractIrGenerator, serializerClassOriginal: ClassDescriptor?, module: ModuleDescriptor, kType: IrType, @@ -901,7 +906,7 @@ interface IrBuilderExtension { } fun IrBuilderWithScope.serializerInstance( - enclosingGenerator: AbstractSerialGenerator, + enclosingGenerator: AbstractIrGenerator, serializerClassOriginal: IrClassSymbol?, pluginContext: SerializationPluginContext, kType: IrType, @@ -960,8 +965,7 @@ interface IrBuilderExtension { thisIrType.arguments.map { val argSer = enclosingGenerator.findTypeSerializerOrContext( compilerContext, - it.typeOrNull!!, //todo: handle star projections here - sourceElement = serializerClassOriginal.owner.sourceElement()?.psi + it.typeOrNull!! //todo: handle star projections here? ) instantiate(argSer, it.typeOrNull!!)!! }) @@ -1076,8 +1080,7 @@ interface IrBuilderExtension { args = kType.arguments.map { val argSer = enclosingGenerator.findTypeSerializerOrContext( pluginContext, - it.typeOrNull!!, // todo: stars - sourceElement = serializerClassOriginal.owner.source.getPsi() + it.typeOrNull!! // todo: stars? ) instantiate(argSer, it.typeOrNull!!) ?: return null } @@ -1242,11 +1245,10 @@ interface IrBuilderExtension { } fun IrClass.findWriteSelfMethod(): IrSimpleFunction? = - declarations.filter { it is IrSimpleFunction && it.name == SerialEntityNames.WRITE_SELF_NAME && !it.isFakeOverride } - .takeUnless(Collection<*>::isEmpty)?.single() as IrSimpleFunction? + declarations.singleOrNull { it is IrSimpleFunction && it.name == SerialEntityNames.WRITE_SELF_NAME && !it.isFakeOverride } as IrSimpleFunction? fun IrBlockBodyBuilder.serializeAllProperties( - generator: AbstractSerialGenerator, + generator: AbstractIrGenerator, serializableIrClass: IrClass, serializableProperties: List, objectToSerialize: IrValueDeclaration, @@ -1339,7 +1341,7 @@ interface IrBuilderExtension { fun IrBlockBodyBuilder.formEncodeDecodePropertyCall( - enclosingGenerator: AbstractSerialGenerator, + enclosingGenerator: AbstractIrGenerator, encoder: IrExpression, property: IrSerializableProperty, whenHaveSerializer: (serializer: IrExpression, sti: IrSerialTypeInfo) -> FunctionWithArgs, 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 ea4df888525..5b57523f523 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 @@ -5,25 +5,23 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.ir.builders.* +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 +import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.types.defaultType -import org.jetbrains.kotlin.ir.types.impl.IrErrorClassImpl.endOffset -import org.jetbrains.kotlin.ir.types.impl.IrErrorClassImpl.startOffset -import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.calls.components.isVararg import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen +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 @@ -34,9 +32,11 @@ class SerializableCompanionIrGenerator( val irClass: IrClass, val serializableIrClass: IrClass, override val compilerContext: SerializationPluginContext, -) : SerializableCompanionCodegen(irClass.descriptor, null), IrBuilderExtension { +) : AbstractIrGenerator(irClass), IrBuilderExtension { - override fun getSerializerGetterDescriptor(): FunctionDescriptor { + private val serializableDescriptor = serializableIrClass.descriptor + + private fun getSerializerGetterDescriptor(): FunctionDescriptor { return irClass.findDeclaration { (it.valueParameters.size == serializableDescriptor.declaredTypeParameters.size && it.valueParameters.all { p -> isKSerializer(p.type) }) && isKSerializer(it.returnType) @@ -46,6 +46,19 @@ class SerializableCompanionIrGenerator( ) } + fun generate() { + val serializerGetterDescriptor = getSerializerGetterDescriptor() + + if (serializableDescriptor.isSerializableObject + || serializableDescriptor.isAbstractOrSealedSerializableClass() + || serializableDescriptor.isSerializableEnum() + ) { + generateLazySerializerGetter(serializerGetterDescriptor) + } else { + generateSerializerGetter(serializerGetterDescriptor) + } + } + companion object { fun generate( irClass: IrClass, @@ -97,7 +110,7 @@ class SerializableCompanionIrGenerator( irSerializableClass.annotations += annotationCtorCall } - override fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) { + fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) { val serializer = requireNotNull( findTypeSerializer( compilerContext, @@ -105,14 +118,14 @@ class SerializableCompanionIrGenerator( ) ) - val kSerializerIrClass = serializer.owner + val kSerializerIrClass = compilerContext.referenceClass(ClassId(SerializationPackages.packageFqName, SerialEntityNames.KSERIALIZER_NAME))!!.owner val targetIrType = kSerializerIrClass.defaultType.substitute(mapOf(kSerializerIrClass.typeParameters[0].symbol to compilerContext.irBuiltIns.anyType)) val property = createLazyProperty(irClass, targetIrType, SerialEntityNames.CACHED_SERIALIZER_PROPERTY_NAME) { val expr = serializerInstance( this@SerializableCompanionIrGenerator, - serializer, compilerContext, kSerializerIrClass.defaultType + serializer, compilerContext, serializableIrClass.defaultType ) patchSerializableClassWithMarkerAnnotation(kSerializerIrClass) +irReturn(requireNotNull(expr)) @@ -124,7 +137,7 @@ class SerializableCompanionIrGenerator( generateSerializerFactoryIfNeeded(methodDescriptor) } - override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) { + fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) { irClass.contributeFunction(methodDescriptor) { getter -> val serializer = requireNotNull( findTypeSerializer( @@ -145,7 +158,7 @@ class SerializableCompanionIrGenerator( } private fun generateSerializerFactoryIfNeeded(getterDescriptor: FunctionDescriptor) { - if (!companionDescriptor.needSerializerFactory()) return + if (!irClass.descriptor.needSerializerFactory()) return val serialFactoryDescriptor = irClass.findDeclaration { it.valueParameters.size == 1 && it.valueParameters.first().isVararg 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 4e51d53476c..b974b207b97 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 @@ -7,7 +7,8 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.backend.common.lower.irThrow import org.jetbrains.kotlin.codegen.CompilationException -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* @@ -24,7 +25,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.backend.common.isStaticSerializable import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* @@ -37,9 +37,9 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SE class SerializableIrGenerator( val irClass: IrClass, override val compilerContext: SerializationPluginContext -) : AbstractSerialGenerator(null, irClass.descriptor), IrBuilderExtension { +) : AbstractIrGenerator(irClass), IrBuilderExtension { - protected val properties = bindingContext.serializablePropertiesForIrBackend(irClass) + protected val properties = serializablePropertiesForIrBackend(irClass) private val serialDescriptorClass = compilerContext.referenceClass( ClassId( @@ -272,7 +272,7 @@ class SerializableIrGenerator( ): Int { check(superClass.isInternalSerializable) val superCtorRef = serializableSyntheticConstructor(superClass)!! - val superProperties = bindingContext.serializablePropertiesForIrBackend(superClass).serializableProperties + val superProperties = serializablePropertiesForIrBackend(superClass).serializableProperties val superSlots = superProperties.bitMaskSlotCount() val arguments = allValueParameters.subList(0, superSlots) + allValueParameters.subList(propertiesStart, propertiesStart + superProperties.size) + @@ -309,7 +309,7 @@ class SerializableIrGenerator( var ignoreIndexTo = -1 val superClass = irClass.getSuperClassOrAny() if (superClass.descriptor.isInternalSerializable) { - ignoreIndexTo = bindingContext.serializablePropertiesForIrBackend(superClass).serializableProperties.size + ignoreIndexTo = serializablePropertiesForIrBackend(superClass).serializableProperties.size // call super.writeSelf var superWriteSelfF = superClass.findWriteSelfMethod() @@ -374,12 +374,7 @@ class SerializableIrGenerator( private fun generateSyntheticMethods() { val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(SAVE) }) { - val func = - irClass.functions.singleOrNull { function -> - function.name == SerialEntityNames.WRITE_SELF_NAME && - function.modality == Modality.FINAL && - function.returnType.isUnit() - } ?: return + val func = irClass.findWriteSelfMethod() ?: return generateWriteSelfMethod(func) } } 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 864da8f55ac..75c45878c9c 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 @@ -5,18 +5,26 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir -import org.jetbrains.kotlin.ir.deepCopyWithVariables import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder import org.jetbrains.kotlin.backend.common.lower.irIfThen import org.jetbrains.kotlin.backend.common.lower.irThrow import org.jetbrains.kotlin.backend.jvm.functionByName import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.codegen.CompilationException -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.deepCopyWithVariables +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrExpressionBody +import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.expressions.mapValueParametersIndexed import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol @@ -32,7 +40,6 @@ 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.fir.SerializationPluginKey import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS @@ -41,8 +48,6 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SA import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_DECODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UNKNOWN_FIELD_EXC -import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorBySerializer -import org.jetbrains.kotlinx.serialization.compiler.resolve.isEnumWithLegacyGeneratedSerializer object SERIALIZABLE_PLUGIN_ORIGIN : IrDeclarationOriginImpl("SERIALIZER", true) @@ -53,18 +58,18 @@ open class SerializerIrGenerator( final override val compilerContext: SerializationPluginContext, metadataPlugin: SerializationDescriptorSerializerPlugin?, private val serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, -) : AbstractSerialGenerator(null, irClass.descriptor), IrBuilderExtension { +) : AbstractIrGenerator(irClass), IrBuilderExtension { protected val serializableIrClass = getSerializableClassDescriptorBySerializer(irClass)!! protected val serialName: String = serializableIrClass.serialName() - protected val properties = bindingContext.serializablePropertiesForIrBackend(serializableIrClass, metadataPlugin) + protected val properties = serializablePropertiesForIrBackend(serializableIrClass, metadataPlugin) protected val serializableProperties = properties.serializableProperties protected val isGeneratedSerializer = irClass.descriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) // TODO TODO protected val generatedSerialDescPropertyDescriptor = getProperty( SerialEntityNames.SERIAL_DESC_FIELD, { true } - )?.takeIf { it.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) } + )?.takeIf { it.isFromPlugin() } protected val anySerialDescProperty = getProperty( SerialEntityNames.SERIAL_DESC_FIELD, @@ -103,7 +108,7 @@ open class SerializerIrGenerator( // how to (auto)create backing field and getter/setter? compilerContext.symbolTable.withReferenceScope(irClass) { - prop = generateSimplePropertyWithBackingField(desc.descriptor, irClass) // TODO check if this works correctly with old FE + prop = generateSimplePropertyWithBackingField(desc.descriptor, irClass) // TODO: Do not use descriptors here localSerializersFieldsDescriptors = findLocalSerializersFieldDescriptors().map { prop -> @@ -600,7 +605,7 @@ open class SerializerIrGenerator( if (!prop && (save || load)) generateSerialDesc() if (serializableIrClass.typeParameters.isNotEmpty()) { - findSerializerConstructorForTypeArgumentsSerializers(irClass)?.let { + findSerializerConstructorForTypeArgumentsSerializers(irClass)?.takeIf { it.owner.isFromPlugin() }?.let { generateGenericFieldsAndConstructor(it.owner) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt index acb58450ca6..0a0ce0d481f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt @@ -6,11 +6,16 @@ package org.jetbrains.kotlinx.serialization.compiler.extensions import org.jetbrains.kotlin.backend.common.ClassLoweringPass +import org.jetbrains.kotlin.backend.common.CompilationException import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.backend.common.runOnFilePostfix +import org.jetbrains.kotlin.backend.jvm.ir.fileParent import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid @@ -56,12 +61,16 @@ private class SerializerClassLowering( private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it } override fun lower(irClass: IrClass) { - SerializableIrGenerator.generate(irClass, context) - SerializerIrGenerator.generate(irClass, context, context.metadataPlugin, serialInfoJvmGenerator) - SerializableCompanionIrGenerator.generate(irClass, context) + try { + SerializableIrGenerator.generate(irClass, context) + SerializerIrGenerator.generate(irClass, context, context.metadataPlugin, serialInfoJvmGenerator) + SerializableCompanionIrGenerator.generate(irClass, context) - if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) { - serialInfoJvmGenerator.generate(irClass) + if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) { + serialInfoJvmGenerator.generate(irClass) + } + } catch (e: Exception) { + throw CompilationException("kotlinx.serialization compiler plugin internal error: unable to transform declaration, see cause", irClass.fileParent, irClass, e) } } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt index 079d2174310..13a2c6d21db 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProv import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen import org.jetbrains.kotlinx.serialization.compiler.resolve.* -import java.util.* open class SerializationResolveExtension @JvmOverloads constructor(val metadataPlugin: SerializationDescriptorSerializerPlugin? = null) : SyntheticResolveExtension { override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List = when { @@ -48,6 +47,16 @@ open class SerializationResolveExtension @JvmOverloads constructor(val metadataP else -> emptyList() } + override fun getSyntheticPropertiesNames(thisDescriptor: ClassDescriptor): List { + // typeSerial0, typeSerial1, ... for serializers of parameterized classes + val count = thisDescriptor.declaredTypeParameters.size + if (count < 1) return emptyList() + val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return emptyList() + if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor, classDescriptor)) return emptyList() + val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" }.map { Name.identifier(it) } + return propNames + } + private fun hasCustomizedSerializeMethod(serializableClass: ClassDescriptor): Boolean { // We cannot check whether companion has @Serializer(MyClass::class) annotation due to recursive resolve problems // (apparently, resolve MyClass type asks for all function names, which leads us to this function again) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt index 9006f685359..c23bc436967 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt @@ -27,7 +27,6 @@ import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.createProjection import org.jetbrains.kotlin.types.typeUtil.makeNullable -import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SimpleSyntheticPropertyDescriptor import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.IMPL_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_CLASS_NAME @@ -220,43 +219,55 @@ object KSerializerDescriptorResolver { } private fun createSerializableClassPropertyDescriptor( - companionDescriptor: ClassDescriptor, - classDescriptor: ClassDescriptor - ): PropertyDescriptor = - doCreateSerializerProperty(companionDescriptor, classDescriptor, SerialEntityNames.SERIAL_DESC_FIELD_NAME) + thisDescriptor: ClassDescriptor, + serializableClassDescriptor: ClassDescriptor + ): PropertyDescriptor { + val typeParam = listOf(createProjection(serializableClassDescriptor.defaultType, Variance.INVARIANT, null)) + val propertyFromSerializer = thisDescriptor.getGeneratedSerializerDescriptor().getMemberScope(typeParam) + .getContributedVariables(SerialEntityNames.SERIAL_DESC_FIELD_NAME, NoLookupLocation.FROM_BUILTINS).single() + val result = doCreateSerializerProperty( + thisDescriptor, + SerialEntityNames.SERIAL_DESC_FIELD_NAME, + propertyFromSerializer.type, + propertyFromSerializer.typeParameters, + DescriptorVisibilities.PUBLIC, + Modality.OPEN // TODO: it was historically OPEN, but I do not see the reasons not to change to FINAL + ) + result.overriddenDescriptors = listOf(propertyFromSerializer) + return result + } private fun doCreateSerializerProperty( - companionDescriptor: ClassDescriptor, - classDescriptor: ClassDescriptor, - name: Name + thisDescriptor: ClassDescriptor, + name: Name, + type: KotlinType, + typeParameters: List = emptyList(), + visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE, + modality: Modality = Modality.FINAL, + needBackingField: Boolean = false ): PropertyDescriptor { - val typeParam = listOf(createProjection(classDescriptor.defaultType, Variance.INVARIANT, null)) - val propertyFromSerializer = companionDescriptor.getGeneratedSerializerDescriptor().getMemberScope(typeParam) - .getContributedVariables(name, NoLookupLocation.FROM_BUILTINS).single() - val propertyDescriptor = PropertyDescriptorImpl.create( - companionDescriptor, Annotations.EMPTY, Modality.OPEN, DescriptorVisibilities.PUBLIC, false, name, - CallableMemberDescriptor.Kind.SYNTHESIZED, companionDescriptor.source, false, false, false, false, false, false + thisDescriptor, Annotations.EMPTY, modality, visibility, false, name, + CallableMemberDescriptor.Kind.SYNTHESIZED, thisDescriptor.source, false, false, false, false, false, false ) val extensionReceiverParameter: ReceiverParameterDescriptor? = null // kludge to disambiguate call propertyDescriptor.setType( - propertyFromSerializer.type, - propertyFromSerializer.typeParameters, - companionDescriptor.thisAsReceiverParameter, + type, + typeParameters, + thisDescriptor.thisAsReceiverParameter, extensionReceiverParameter, emptyList() ) val propertyGetter = PropertyGetterDescriptorImpl( - propertyDescriptor, Annotations.EMPTY, Modality.OPEN, DescriptorVisibilities.PUBLIC, false, false, false, - CallableMemberDescriptor.Kind.SYNTHESIZED, null, companionDescriptor.source + propertyDescriptor, Annotations.EMPTY, modality, visibility, false, false, false, + CallableMemberDescriptor.Kind.SYNTHESIZED, null, thisDescriptor.source ) + propertyGetter.initialize(type) - propertyGetter.initialize(propertyFromSerializer.type) - propertyDescriptor.initialize(propertyGetter, null) - propertyDescriptor.overriddenDescriptors = listOf(propertyFromSerializer) - + val backingField = if (needBackingField) FieldDescriptorImpl(Annotations.EMPTY, propertyDescriptor) else null + propertyDescriptor.initialize(propertyGetter, null, backingField, null) return propertyDescriptor } @@ -650,7 +661,7 @@ object KSerializerDescriptorResolver { serializerClass, listOf(TypeProjectionImpl(param.defaultType)) ) - val desc = SimpleSyntheticPropertyDescriptor(serializerDescriptor, "$typeArgPrefix$index", pType) + val desc = doCreateSerializerProperty(serializerDescriptor, Name.identifier("$typeArgPrefix$index"), pType, needBackingField = true) return listOf(desc) } } 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 d5ed7565ffc..61451453d7b 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 @@ -165,7 +165,10 @@ internal val ISerializableProperties<*, *, *>.goldenMaskList: List internal fun List>.bitMaskSlotCount() = size / 32 + 1 internal fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32 -internal fun BindingContext.serializablePropertiesFor(classDescriptor: ClassDescriptor, serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null): SerializableProperties { +internal fun BindingContext.serializablePropertiesFor( + classDescriptor: ClassDescriptor, + serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null +): SerializableProperties { val props = this.get(SERIALIZABLE_PROPERTIES, classDescriptor) ?: SerializableProperties(classDescriptor, this) serializationDescriptorSerializer?.putIfNeeded(classDescriptor, props) return props @@ -187,59 +190,59 @@ class IrSerializableProperties( ) : ISerializableProperties { } -internal fun BindingContext?.serializablePropertiesForIrBackend( +internal fun serializablePropertiesForIrBackend( classDescriptor: IrClass, serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null ): IrSerializableProperties { - if (this != null) { - // can work with old FE - TODO("Support this for old FE") - } else { - 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 - .filter { !it.isFakeOverride && !it.isDelegated } - .filter(::isPropSerializable) - .map { - val isConstructorParameterWithDefault = primaryParamsAsProps[it] ?: false - IrSerializableProperty( - it, - isConstructorParameterWithDefault, - it.backingField != null, - it.backingField?.initializer.let { init -> init != null && !init.expression.isInitializePropertyFromParameter() } || isConstructorParameterWithDefault - ) - } - .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) + 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? { 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 3f53f4ebb3f..d51337aa41b 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 @@ -19,24 +19,17 @@ 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.fir.scopes.impl.overrides import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.util.hasAnnotation -import org.jetbrains.kotlin.ir.util.module -import org.jetbrains.kotlin.psi.KtDeclarationWithInitializer -import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.asSimpleType 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.overridenSerializer 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, @@ -48,7 +41,7 @@ class SerializableProperty( override val type = descriptor.type override val genericIndex = type.genericIndex val module = descriptor.module - override val serializableWith = descriptor.serializableWith ?: analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType + val serializableWith = descriptor.serializableWith ?: analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType override val optional = !descriptor.annotations.serialRequired && declaresDefaultValue override val transient = descriptor.annotations.serialTransient || !hasBackingField val annotationsWithArguments: List, List>> = @@ -61,7 +54,6 @@ interface ISerializableProperty { val name: String val type: T val genericIndex: Int? - val serializableWith: T? val optional: Boolean val transient: Boolean } @@ -75,7 +67,7 @@ class IrSerializableProperty( override val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString() override val type = descriptor.getter!!.returnType as IrSimpleType override val genericIndex = type.genericIndex - override val serializableWith = type.overridenSerializer /* ?:analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType */ // TODO + 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 diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java index 171ca8fcd15..f000c8a2141 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java @@ -6,10 +6,9 @@ package org.jetbrains.kotlinx.serialization; import com.intellij.testFramework.TestDataPath; -import org.jetbrains.kotlin.test.util.KtTestUtil; import org.jetbrains.kotlin.test.TargetBackend; import org.jetbrains.kotlin.test.TestMetadata; -import org.junit.jupiter.api.Nested; +import org.jetbrains.kotlin.test.util.KtTestUtil; import org.junit.jupiter.api.Test; import java.io.File; @@ -25,12 +24,24 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("annotationsOnFile.kt") + public void testAnnotationsOnFile() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/annotationsOnFile.kt"); + } + @Test @TestMetadata("classSerializerAsObject.kt") public void testClassSerializerAsObject() throws Exception { runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/classSerializerAsObject.kt"); } + @Test + @TestMetadata("generics.kt") + public void testGenerics() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/generics.kt"); + } + @Test @TestMetadata("metaSerializable.kt") public void testMetaSerializable() throws Exception { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/annotationsOnFile.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/annotationsOnFile.kt new file mode 100644 index 00000000000..764082ce70a --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/annotationsOnFile.kt @@ -0,0 +1,78 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM_IR + +// WITH_STDLIB + +// FILE: a.kt + +package a + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +object MultiplyingIntSerializer : KSerializer { + override val descriptor: SerialDescriptor + get() = PrimitiveSerialDescriptor("MultiplyingInt", PrimitiveKind.INT) + + override fun deserialize(decoder: Decoder): Int { + return decoder.decodeInt() / 2 + } + + override fun serialize(encoder: Encoder, value: Int) { + encoder.encodeInt(value * 2) + } +} + +data class Cont(val i: Int) + +object ContSerializer: KSerializer { + override fun deserialize(decoder: Decoder): Cont { + return Cont(decoder.decodeInt()) + } + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ContSerializer", PrimitiveKind.INT) + + override fun serialize(encoder: Encoder, value: Cont) { + encoder.encodeInt(value.i) + } +} + +// FILE: test.kt + +@file:UseContextualSerialization(Cont::class) +@file:UseSerializers(MultiplyingIntSerializer::class) + +package a + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* + +@Serializable +class Holder( + val i: Int, + val c: Cont +) + +fun testOnFile(): String { + val j = Json { + serializersModule = SerializersModule { + contextual(ContSerializer) + } + } + val h = Holder(3, Cont(4)) + val str = j.encodeToString( + Holder.serializer(), + h + ) + if ("""{"i":6,"c":4}""" != str) return str + val decoded = j.decodeFromString(Holder.serializer(), str) + if (decoded.i != h.i) return "i: ${decoded.i}" + if (decoded.c.i != h.c.i) return "c.i: ${decoded.c.i}" + return "OK" +} + +fun box(): String { + return testOnFile() +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/generics.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/generics.kt new file mode 100644 index 00000000000..3e523c7a09c --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/generics.kt @@ -0,0 +1,22 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM_IR + +// WITH_STDLIB + +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +@Serializable +data class Foo(val i: Int, val t: T? = null) + +@Serializable +class Holder(val f1: Foo, val f2: Foo) + +fun box(): String { + val holder = Holder(Foo(1, "1"), Foo(2)) + val str = Json.encodeToString(Holder.serializer(), holder) + if (str != """{"f1":{"i":1,"t":"1"},"f2":{"i":2}}""") return str + val decoded = Json.decodeFromString(Holder.serializer(), str) + if (decoded.f1.t != holder.f1.t) return "f1.t: ${decoded.f1.t}" + return "OK" +} \ No newline at end of file