From 16dbafced1020ffab2542862caad8d274c07faa7 Mon Sep 17 00:00:00 2001 From: Sergey Shanshin Date: Wed, 26 May 2021 16:07:59 +0300 Subject: [PATCH] Add `.serializer()` to enum companion and object with custom serializer Fixes Kotlin/kotlinx.serialization#1386 --- .../compiler/backend/common/TypeUtil.kt | 6 ++--- .../backend/ir/SerializerIrGenerator.kt | 2 +- .../backend/js/SerializerJsTranslator.kt | 4 ++-- .../backend/jvm/SerializerCodegenImpl.kt | 2 +- .../compiler/resolve/KSerializationUtil.kt | 22 +++++++++++-------- .../resolve/SerializableProperties.kt | 2 +- 6 files changed, 21 insertions(+), 17 deletions(-) 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 688f29f7e07..a004f547ef3 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 @@ -43,7 +43,7 @@ fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module return null } -fun KotlinType.isSerializableObject() = +fun KotlinType.isGeneratedSerializableObject() = toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs } == true @Suppress("FunctionName", "LocalVariableName") @@ -151,7 +151,7 @@ fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescri if (userOverride != null) return userOverride.toClassDescriptor if (kType.isTypeParameter()) return null if (KotlinBuiltIns.isArray(kType)) return module.getClassFromInternalSerializationPackage(SpecialBuiltins.referenceArraySerializer) - if (kType.isSerializableObject()) return module.getClassFromInternalSerializationPackage(SpecialBuiltins.objectSerializer) + if (kType.isGeneratedSerializableObject()) return module.getClassFromInternalSerializationPackage(SpecialBuiltins.objectSerializer) val stdSer = findStandardKotlinTypeSerializer(module, kType) // see if there is a standard serializer ?: findEnumTypeSerializer(module, kType) if (stdSer != null) return stdSer @@ -216,7 +216,7 @@ fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType fun findEnumTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? { val classDescriptor = kType.toClassDescriptor ?: return null - return if (classDescriptor.kind == ClassKind.ENUM_CLASS && !classDescriptor.isSerializableEnum()) + return if (classDescriptor.kind == ClassKind.ENUM_CLASS && !classDescriptor.isInternallySerializableEnum()) module.findClassAcrossModuleDependencies(enumSerializerId) else null } 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 29336613039..5b26c806398 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 @@ -572,7 +572,7 @@ open class SerializerIrGenerator( ) { val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return val generator = when { - serializableDesc.isSerializableEnum() -> SerializerForEnumsGenerator(irClass, context, bindingContext, serialInfoJvmGenerator) + serializableDesc.isInternallySerializableEnum() -> SerializerForEnumsGenerator(irClass, context, bindingContext, serialInfoJvmGenerator) serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context, bindingContext, serialInfoJvmGenerator) else -> SerializerIrGenerator(irClass, context, bindingContext, metadataPlugin, serialInfoJvmGenerator) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt index 75e792df367..726a2d7a233 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt @@ -373,11 +373,11 @@ open class SerializerJsTranslator( metadataPlugin: SerializationDescriptorSerializerPlugin? ) { val serializableDesc = getSerializableClassDescriptorBySerializer(descriptor) ?: return - if (serializableDesc.isSerializableEnum()) { + if (serializableDesc.isInternallySerializableEnum()) { SerializerForEnumsTranslator(descriptor, translator, context).generate() } else { SerializerJsTranslator(descriptor, translator, context, metadataPlugin).generate() } } } -} \ No newline at end of file +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt index 9d02e6ba726..785bece6875 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt @@ -38,7 +38,7 @@ open class SerializerCodegenImpl( companion object { fun generateSerializerExtensions(codegen: ImplementationBodyCodegen, metadataPlugin: SerializationDescriptorSerializerPlugin?) { val serializableClass = getSerializableClassDescriptorBySerializer(codegen.descriptor) ?: return - val serializerCodegen = if (serializableClass.isSerializableEnum()) { + val serializerCodegen = if (serializableClass.isInternallySerializableEnum()) { SerializerForEnumsCodegen(codegen, serializableClass) } else { SerializerCodegenImpl(codegen, serializableClass, metadataPlugin) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt index 546af42355b..e2a42f0a98e 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt @@ -97,9 +97,12 @@ val KotlinType?.toClassDescriptor: ClassDescriptor? } internal val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean - get() = this.isSerializableObject || this.isSerializableEnum() || this.kind == ClassKind.CLASS && annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName) + get() = this.isSerializableObject || this.isSerializableEnum() || this.kind == ClassKind.CLASS && hasSerializableAnnotation internal val ClassDescriptor.isSerializableObject: Boolean + get() = kind == ClassKind.OBJECT && hasSerializableAnnotation + +internal val ClassDescriptor.isInternallySerializableObject: Boolean get() = kind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs internal val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking @@ -108,14 +111,12 @@ internal val ClassDescriptor.isInternalSerializable: Boolean //todo normal check return hasSerializableAnnotationWithoutArgs } -internal fun ClassDescriptor.isSerializableEnum(): Boolean { - if (this.kind != ClassKind.ENUM_CLASS) return false - if (hasSerializableAnnotationWithoutArgs) return true - return false -} +internal fun ClassDescriptor.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableAnnotation + +internal fun ClassDescriptor.isInternallySerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableAnnotationWithoutArgs internal val ClassDescriptor.shouldHaveGeneratedSerializer: Boolean - get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isSerializableEnum() + get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isInternallySerializableEnum() internal fun ClassDescriptor.enumEntries(): List { check(this.kind == ClassKind.ENUM_CLASS) @@ -128,9 +129,12 @@ internal fun ClassDescriptor.enumEntries(): List { internal val Annotations.hasAnySerialAnnotation: Boolean get() = serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true } +private val ClassDescriptor.hasSerializableAnnotation + get() = annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName) + internal val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean get() { - if (!annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) return false + if (!hasSerializableAnnotation) return false // If provided descriptor is lazy, carefully look at psi in order not to trigger full resolve which may be recursive. // Otherwise, this descriptor is deserialized from another module and it is OK to check value right away. val psi = findSerializableAnnotationDeclaration() ?: return (serializableWith == null) @@ -178,7 +182,7 @@ internal val ClassDescriptor?.classSerializer: ClassDescriptor? } internal val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean - get() = isSerializableObject || companionObjectDescriptor?.serializerForClass == this.defaultType + get() = isInternallySerializableObject || companionObjectDescriptor?.serializerForClass == this.defaultType // returns only user-overriden Serializer internal val KotlinType.overridenSerializer: KotlinType? 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 08d13828810..5e591d36d90 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 @@ -68,7 +68,7 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val .let { unsort(serializableClass, it) } isExternallySerializable = - serializableClass.isSerializableEnum() || primaryConstructorParameters.size == primaryConstructorProperties.size + serializableClass.isInternallySerializableEnum() || primaryConstructorParameters.size == primaryConstructorProperties.size }