From dc7ba5d22b90e900ebbec5b5798453ca3ab21af7 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Wed, 17 Aug 2022 12:41:23 +0200 Subject: [PATCH] Remove module-wide ObsoleteDescriptorApi opt-in and remove access to .descriptor whenever possible --- .../build.gradle.kts | 1 - .../compiler/backend/ir/BaseIrGenerator.kt | 30 +++++++++------ .../compiler/backend/ir/DefaultValuesUtils.kt | 2 + .../backend/ir/IrBuilderWithPluginContext.kt | 23 +++++------ .../compiler/backend/ir/IrPredicates.kt | 38 ++++++++++++++++--- .../backend/ir/IrSerializableProperties.kt | 2 + .../ir/SerialInfoImplJvmIrGenerator.kt | 8 +++- .../ir/SerializableCompanionIrGenerator.kt | 6 +-- .../backend/ir/SerializableIrGenerator.kt | 35 ++++++++--------- .../backend/ir/SerializerForEnumsGenerator.kt | 5 +-- .../backend/ir/SerializerIrGenerator.kt | 17 +++------ .../SerializationLoweringExtension.kt | 2 + .../testData/boxIr/enumsAreCached.kt | 15 +++++++- 13 files changed, 114 insertions(+), 70 deletions(-) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts b/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts index 1d44f3266f9..e8b354e4b0b 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts @@ -60,7 +60,6 @@ sourceSets { tasks.withType> { kotlinOptions { - freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI" freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi" } } 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 index 15149bc5ca5..0e143bd8dd7 100644 --- 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 @@ -11,7 +11,6 @@ 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 @@ -39,19 +38,26 @@ 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 { +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 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 throwMissedFieldExceptionArrayFunc = compilerContext.referenceFunctions( + CallableId( + SerializationPackages.internalPackageFqName, + SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME + ) + ).singleOrNull() - private val enumSerializerFactoryFunc - = compilerContext.enumSerializerFactoryFunc + private val enumSerializerFactoryFunc = compilerContext.enumSerializerFactoryFunc - private val markedEnumSerializerFactoryFunc - = compilerContext.markedEnumSerializerFactoryFunc + private val markedEnumSerializerFactoryFunc = compilerContext.markedEnumSerializerFactoryFunc fun useFieldMissingOptimization(): Boolean { return throwMissedFieldExceptionFunc != null && throwMissedFieldExceptionArrayFunc != null @@ -250,7 +256,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override genericGetter ) val (functionToCall, args: List) = if (innerSerial != null) whenHaveSerializer(innerSerial, sti) else whenDoNot(sti) - val typeArgs = if (functionToCall.descriptor.typeParameters.isNotEmpty()) listOf(property.type) else listOf() + val typeArgs = if (functionToCall.owner.typeParameters.isNotEmpty()) listOf(property.type) else listOf() return irInvoke(encoder, functionToCall, typeArguments = typeArgs, valueArguments = args, returnTypeHint = returnTypeHint) } @@ -267,7 +273,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override val adjustedArgs: List = // if typeArgs.size == args.size then the serializer is custom - we need to use the actual serializers from the arguments - if ((typeArgs.size != args.size) && (baseClass.descriptor.isSealed() || baseClass.descriptor.modality == Modality.ABSTRACT)) { + if ((typeArgs.size != args.size) && (baseClass.modality == Modality.SEALED || baseClass.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) } 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 index 43c07c6ac5a..6cef9f408f1 100644 --- 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 @@ -6,6 +6,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irGetField @@ -78,6 +79,7 @@ fun IrBuilderWithScope.createPropertyByParamReplacer( 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. */ +@OptIn(ObsoleteDescriptorBasedAPI::class) fun createInitializerAdapter( irClass: IrClass, paramGetReplacer: (ValueParameterDescriptor) -> IrExpression?, 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 index 757f144102b..152864802ed 100644 --- 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 @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* @@ -27,16 +28,12 @@ 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.jvm.JvmPrimitiveType import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_FQ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_MODE_FQ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_PUBLICATION_MODE_NAME -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 { @@ -134,7 +131,7 @@ interface IrBuilderWithPluginContext { } fun IrClass.contributeAnonymousInitializer(bodyGen: IrBlockBodyBuilder.() -> Unit) { - val symbol = IrAnonymousInitializerSymbolImpl(descriptor) + val symbol = IrAnonymousInitializerSymbolImpl(symbol) factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also { it.parent = this declarations.add(it) @@ -256,9 +253,6 @@ interface IrBuilderWithPluginContext { 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) @@ -269,7 +263,7 @@ interface IrBuilderWithPluginContext { fun IrBuilderWithScope.generateAnySuperConstructorCall(toBuilder: IrBlockBodyBuilder) { val anyConstructor = compilerContext.irBuiltIns.anyClass.owner.declarations.single { it is IrConstructor } as IrConstructor with(toBuilder) { - +IrDelegatingConstructorCallImpl.fromSymbolDescriptor( + +IrDelegatingConstructorCallImpl.fromSymbolOwner( startOffset, endOffset, compilerContext.irBuiltIns.unitType, anyConstructor.symbol @@ -348,14 +342,14 @@ interface IrBuilderWithPluginContext { createClassReference(classSymbol.starProjectedType, startOffset, endOffset) fun collectSerialInfoAnnotations(irClass: IrClass): List { - if (!(irClass.isInterface || irClass.descriptor.hasSerializableOrMetaAnnotation)) return emptyList() + if (!(irClass.isInterface || irClass.hasSerializableOrMetaAnnotation())) return emptyList() val annotationByFq: MutableMap = - irClass.annotations.associateBy { it.symbol.owner.parentAsClass.descriptor.fqNameSafe }.toMutableMap() + irClass.annotations.associateBy { it.symbol.owner.parentAsClass.fqNameWhenAvailable!! }.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 + val parent = it.symbol.owner.parentAsClass + if (parent.isInheritableSerialInfoAnnotation) parent.fqNameWhenAvailable!! to it else null } annotations.forEach { (fqname, call) -> if (fqname !in annotationByFq) { @@ -371,7 +365,7 @@ interface IrBuilderWithPluginContext { fun IrBuilderWithScope.copyAnnotationsFrom(annotations: List): List = annotations.mapNotNull { annotationCall -> val annotationClass = annotationCall.symbol.owner.parentAsClass - if (!annotationClass.descriptor.isSerialInfoAnnotation) return@mapNotNull null + if (!annotationClass.isSerialInfoAnnotation) return@mapNotNull null if (compilerContext.platform.isJvm()) { val implClass = compilerContext.serialInfoImplJvmIrGenerator.getImplClass(annotationClass) @@ -389,6 +383,7 @@ interface IrBuilderWithPluginContext { } } + @OptIn(ObsoleteDescriptorBasedAPI::class) fun IrBuilderWithScope.wrapperClassReference(classType: IrType): IrClassReference { if (compilerContext.platform.isJvm()) { // "Byte::class" -> "java.lang.Byte::class" 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 index ea85137e2b7..df915ec5aa5 100644 --- 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 @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrExpression @@ -23,11 +24,11 @@ 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.kotlin.platform.js.isJs +import org.jetbrains.kotlin.platform.konan.isNative import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext 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.* internal fun IrType.isKSerializer(): Boolean { val simpleType = this as? IrSimpleType ?: return false @@ -89,7 +90,7 @@ private fun IrClass.checkSerializableOrMetaAnnotationArgs(mustDoNotHaveArgs: Boo return true } return annotations - .map { it.annotationClass.annotations } + .map { it.constructedClass.annotations } .any { it.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) } } @@ -98,6 +99,9 @@ internal val IrClass.isSerialInfoAnnotation: Boolean || annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName) || annotations.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) +internal val IrClass.isInheritableSerialInfoAnnotation: Boolean + get() = annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName) + internal fun IrClass.shouldHaveGeneratedSerializer(context: SerializationPluginContext): Boolean = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isEnumWithLegacyGeneratedSerializer(context) @@ -142,6 +146,7 @@ fun IrClass.getSuperClassNotAny(): IrClass? { return if (parentClass.defaultType.isAny()) null else parentClass } +@OptIn(ObsoleteDescriptorBasedAPI::class) internal fun IrDeclaration.isFromPlugin(): Boolean = this.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) || (this.descriptor as? CallableMemberDescriptor)?.kind == CallableMemberDescriptor.Kind.SYNTHESIZED // old FE doesn't specify origin @@ -165,14 +170,35 @@ fun IrClass.findSerializableSyntheticConstructor(): IrConstructorSymbol? { return declarations.filterIsInstance().singleOrNull { it.isSerializationCtor() }?.symbol } +internal fun IrClass.needSerializerFactory(compilerContext: SerializationPluginContext): Boolean { + if (!(compilerContext.platform?.isNative() == true || compilerContext.platform.isJs())) return false + val serializableClass = getSerializableClassDescriptorByCompanion(this) ?: return false + if (serializableClass.isSerializableObject) return true + if (serializableClass.isSerializableEnum()) return true + if (serializableClass.isAbstractOrSealedSerializableClass) return true + if (serializableClass.isSealedSerializableInterface) return true + if (serializableClass.typeParameters.isEmpty()) return false + return true +} + + +internal fun getSerializableClassDescriptorByCompanion(companion: IrClass): IrClass? { + if (companion.isSerializableObject) return companion + if (!companion.isCompanion) return null + val classDescriptor = (companion.parent as? IrClass) ?: return null + if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null + return classDescriptor +} + + internal fun IrExpression.isInitializePropertyFromParameter(): Boolean = this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER -internal val IrConstructorCall.annotationClass +internal val IrConstructorCall.constructedClass get() = this.symbol.owner.constructedClass internal val List.hasAnySerialAnnotation: Boolean - get() = serialNameValue != null || any { it.annotationClass.isSerialInfoAnnotation == true } + get() = serialNameValue != null || any { it.constructedClass.isSerialInfoAnnotation } internal val List.serialNameValue: String? get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)?.getStringConstArgument(0) // @SerialName("foo") 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 index 97538e7d825..be8e497c810 100644 --- 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 @@ -6,6 +6,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.types.IrSimpleType @@ -41,6 +42,7 @@ class IrSerializableProperties( override val serializableStandaloneProperties: List ) : ISerializableProperties +@OptIn(ObsoleteDescriptorBasedAPI::class) internal fun serializablePropertiesForIrBackend( irClass: IrClass, serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null 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 e0fdd6185f0..f7cf52e22b2 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 @@ -8,6 +8,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI import org.jetbrains.kotlin.backend.common.ir.addExtensionReceiver import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* @@ -30,12 +31,14 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames // This doesn't support annotation arguments of type KClass and Array because the codegen doesn't compute JVM signatures for // such cases correctly (because inheriting from annotation classes is prohibited in Kotlin). // Currently it results in an "accidental override" error where a method with return type KClass conflicts with the one with Class. +@OptIn(ObsoleteDescriptorBasedAPI::class) class SerialInfoImplJvmIrGenerator( private val context: SerializationPluginContext, private val moduleFragment: IrModuleFragment, @@ -53,7 +56,7 @@ class SerialInfoImplJvmIrGenerator( fun getImplClass(serialInfoAnnotationClass: IrClass): IrClass = annotationToImpl.getOrPut(serialInfoAnnotationClass) { - @OptIn(FirIncompatiblePluginAPI::class) // TODO + @OptIn(FirIncompatiblePluginAPI::class) val implClassSymbol = context.referenceClass(serialInfoAnnotationClass.kotlinFqName.child(SerialEntityNames.IMPL_NAME)) implClassSymbol!!.owner.apply(this::generate) } @@ -112,6 +115,9 @@ class SerialInfoImplJvmIrGenerator( putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name)) } + @FirIncompatiblePluginAPI + fun KotlinType.toIrType() = compilerContext.typeTranslator.translateType(this) + private fun IrType.kClassToJClassIfNeeded(): IrType = when { this.isKClass() -> javaLangType this.isKClassArray() -> compilerContext.irBuiltIns.arrayClass.typeWith(javaLangType) 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 ec20e8030bc..e55a6562470 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 @@ -83,14 +83,14 @@ class SerializableCompanionIrGenerator( val irSerializableClass = if (irClass.isCompanion) irClass.parentAsClass else irClass val serializableWithAlreadyPresent = irSerializableClass.annotations.any { - it.symbol.descriptor.constructedClass.fqNameSafe == annotationMarkerClass.owner.fqNameWhenAvailable + it.constructedClass.fqNameWhenAvailable == annotationMarkerClass.owner.fqNameWhenAvailable } if (serializableWithAlreadyPresent) return val annotationCtor = annotationMarkerClass.constructors.single { it.owner.isPrimary } val annotationType = annotationMarkerClass.defaultType - val annotationCtorCall = IrConstructorCallImpl.fromSymbolDescriptor(startOffset, endOffset, annotationType, annotationCtor).apply { + val annotationCtorCall = IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, annotationType, annotationCtor).apply { putValueArgument( 0, createClassReference( @@ -150,7 +150,7 @@ class SerializableCompanionIrGenerator( } private fun generateSerializerFactoryIfNeeded(getterDescriptor: IrSimpleFunction) { - if (!irClass.descriptor.needSerializerFactory()) return + if (!irClass.needSerializerFactory(compilerContext)) 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 f433c00ff2e..e76b50f3c22 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 @@ -6,7 +6,6 @@ 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.ClassKind import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.ir.IrStatement @@ -19,12 +18,10 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.getOrPutNullable -import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_DESCRIPTOR_FIELD_NAME @@ -97,8 +94,9 @@ class SerializableIrGenerator( } // Missing field exception parts - val exceptionCtorRef = compilerContext.referenceConstructors(ClassId(SerializationPackages.packageFqName, Name.identifier(MISSING_FIELD_EXC))) - .single { it.owner.valueParameters.singleOrNull()?.type?.isString() == true } + val exceptionCtorRef = + compilerContext.referenceConstructors(ClassId(SerializationPackages.packageFqName, Name.identifier(MISSING_FIELD_EXC))) + .single { it.owner.valueParameters.singleOrNull()?.type?.isString() == true } val exceptionType = exceptionCtorRef.owner.returnType val seenVarsOffset = serializableProperties.bitMaskSlotCount() @@ -244,7 +242,7 @@ class SerializableIrGenerator( ?: error("Non-serializable parent of serializable $irClass must have no arg constructor") - val call = IrDelegatingConstructorCallImpl.fromSymbolDescriptor( + val call = IrDelegatingConstructorCallImpl.fromSymbolOwner( startOffset, endOffset, compilerContext.irBuiltIns.unitType, @@ -270,13 +268,14 @@ class SerializableIrGenerator( propertiesStart: Int ): Int { check(superClass.isInternalSerializable) - val superCtorRef = superClass.findSerializableSyntheticConstructor() ?: error("Class serializable internally should have special constructor with marker") + val superCtorRef = superClass.findSerializableSyntheticConstructor() + ?: error("Class serializable internally should have special constructor with marker") val superProperties = serializablePropertiesForIrBackend(superClass).serializableProperties val superSlots = superProperties.bitMaskSlotCount() val arguments = allValueParameters.subList(0, superSlots) + allValueParameters.subList(propertiesStart, propertiesStart + superProperties.size) + allValueParameters.last() // SerializationConstructorMarker - val call = IrDelegatingConstructorCallImpl.fromSymbolDescriptor( + val call = IrDelegatingConstructorCallImpl.fromSymbolOwner( startOffset, endOffset, compilerContext.irBuiltIns.unitType, @@ -307,7 +306,7 @@ class SerializableIrGenerator( // Compute offset of properties in superclass var ignoreIndexTo = -1 val superClass = irClass.getSuperClassOrAny() - if (superClass.descriptor.isInternalSerializable) { + if (superClass.isInternalSerializable) { ignoreIndexTo = serializablePropertiesForIrBackend(superClass).serializableProperties.size // call super.writeSelf @@ -380,16 +379,18 @@ class SerializableIrGenerator( irClass: IrClass, context: SerializationPluginContext, ) { - val serializableClass = irClass.descriptor - - if (serializableClass.isInternalSerializable) { + if (irClass.isInternalSerializable) { SerializableIrGenerator(irClass, context).generate() irClass.patchDeclarationParents(irClass.parent) - } else if (serializableClass.serializableAnnotationIsUseless) { - throw CompilationException( - "@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " + - "Provide serializer manually via e.g. companion object", null, serializableClass.findPsi() - ) + } else { + val serializableAnnotationIsUseless = with(irClass) { + hasSerializableOrMetaAnnotationWithoutArgs() && !isInternalSerializable && !hasCompanionObjectAsSerializer && kind != ClassKind.ENUM_CLASS && !isSealedSerializableInterface + } + if (serializableAnnotationIsUseless) + throw AssertionError( + "@Serializable annotation on $irClass would be ignored because it is impossible to serialize it automatically. " + + "Provide serializer manually via e.g. companion object" + ) } } } 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 9a7200004aa..be92d98359b 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 @@ -54,8 +54,7 @@ class SerializerForEnumsGenerator( val decode = decoderClass.functionByName(CallingConventions.decodeEnum) val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol) - val serializableIrClass = requireNotNull(serializableIrClass) { "Enums do not support external serialization" } - val valuesF = serializableIrClass.functions.single { it.name == StandardNames.ENUM_VALUES } + val valuesF = this@SerializerForEnumsGenerator.serializableIrClass.functions.single { it.name == StandardNames.ENUM_VALUES } val getValues = irInvoke(dispatchReceiver = null, callee = valuesF.symbol) @@ -67,7 +66,7 @@ class SerializerForEnumsGenerator( getValues, arrayGet.symbol, irInvoke(irGet(loadFunc.valueParameters[0]), decode, serialDescGetter), - typeHint = serializableIrClass.defaultType + typeHint = this@SerializerForEnumsGenerator.serializableIrClass.defaultType ) +irReturn(getValueByOrdinal) } 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 651d2bb9a6f..87fa34d2fbc 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 @@ -16,14 +16,11 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* 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.* 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 @@ -117,7 +114,7 @@ open class SerializerIrGenerator( } val anonymousInit = irClass.run { - val symbol = IrAnonymousInitializerSymbolImpl(descriptor) + val symbol = IrAnonymousInitializerSymbolImpl(symbol) irClass.factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also { it.parent = this declarations.add(it) @@ -207,7 +204,7 @@ open class SerializerIrGenerator( addFunctionBody(typedConstructorDescriptor) { ctor -> // generate call to primary ctor to init serialClassDesc and super() val primaryCtor = irClass.constructors.primary - +IrDelegatingConstructorCallImpl.fromSymbolDescriptor( + +IrDelegatingConstructorCallImpl.fromSymbolOwner( startOffset, endOffset, compilerContext.irBuiltIns.unitType, @@ -268,15 +265,11 @@ open class SerializerIrGenerator( val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc") - // fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): StructureEncoder + // public fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder val beginFunc = encoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 } - val call = irCall(beginFunc, type = kOutputClass.defaultType).mapValueParametersIndexed { _, _ -> - irGet(localSerialDesc) - } - // can it be done in more concise way? e.g. additional builder function? - call.dispatchReceiver = irGet(saveFunc.valueParameters[0]) + val call = irInvoke(irGet(saveFunc.valueParameters[0]), beginFunc, irGet(localSerialDesc), typeHint = kOutputClass.defaultType) val objectToSerialize = saveFunc.valueParameters[1] val localOutput = irTemporary(call, "output") 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 4ba2a93c8f2..7e466c58554 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 @@ -12,6 +12,7 @@ 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.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid @@ -93,6 +94,7 @@ private class SerializerClassLowering( SerializerIrGenerator.generate(irClass, context, context.metadataPlugin) SerializableCompanionIrGenerator.generate(irClass, context) + @OptIn(ObsoleteDescriptorBasedAPI::class) if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) { serialInfoJvmGenerator.generate(irClass) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/enumsAreCached.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/enumsAreCached.kt index b336e19f93f..a0814df6729 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/enumsAreCached.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/enumsAreCached.kt @@ -20,7 +20,7 @@ enum class Plain { class Holder(val p: Plain, val w: WithNames) @OptIn(InternalSerializationApi::class) -fun box(): String { +fun testSerializers(): String { val cs = (Holder.serializer() as GeneratedSerializer<*>).childSerializers() val str1 = cs[0].toString() if (!str1.contains("kotlinx.serialization.internal.EnumSerializer")) return str1 @@ -32,4 +32,17 @@ fun box(): String { // val str2 = cs[1].toString() // if (!str2.contains("kotlinx.serialization.internal.EnumSerializer")) return str2 return "OK" +} + +fun testSerialization(previous: String): String { + if (previous != "OK") return previous + val h = Holder(Plain.B, WithNames.ENTRY1) + val s = Json.encodeToString(Holder.serializer(), h) + if (s != """{"p":"B","w":"A"}""") return s + if (Json.decodeFromString(Holder.serializer(), s).w != WithNames.ENTRY1) return "Deserialization failure" + return "OK" +} + +fun box(): String { + return testSerialization(testSerializers()) } \ No newline at end of file