From 89a8bdc0f6c1ed475aa770173f04f4c885ee9190 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Mon, 15 Aug 2022 18:46:53 +0200 Subject: [PATCH] Support enum serialization in FIR for all runtime versions, re-enable optimization in IR backend by correctly determining presence of functions in runtime. --- .../build.gradle.kts | 4 + .../compiler/backend/ir/BaseIrGenerator.kt | 6 +- .../compiler/backend/ir/IrPredicates.kt | 12 +- .../backend/ir/SerializerIrGenerator.kt | 8 +- .../backend/ir/SerializerSearchUtil.kt | 8 +- .../SerializationLoweringExtension.kt | 19 +++ .../fir/SerializationFirResolveExtension.kt | 22 ++- .../compiler/fir/SerializationFirUtils.kt | 14 +- ...SerializationFirBlackBoxTestGenerated.java | 6 + .../SerializationFirMembersTestGenerated.java | 6 + .../SerializationIrBoxTestGenerated.java | 6 + .../testData/boxIr/enumsAreCached.kt | 35 +++++ .../testData/firMembers/enums.fir.txt | 132 ++++++++++++++++++ .../testData/firMembers/enums.kt | 45 ++++++ 14 files changed, 291 insertions(+), 32 deletions(-) create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/enumsAreCached.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/enums.fir.txt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/enums.kt diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts b/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts index 785aaedead2..1d44f3266f9 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts @@ -5,6 +5,10 @@ plugins { id("jps-compatible") } +repositories { + mavenLocal() +} + dependencies { compileOnly(intellijCore()) compileOnly(commonDependency("org.jetbrains.intellij.deps:asm-all")) 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 c6e81dd445c..15149bc5ca5 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 @@ -48,10 +48,10 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME)).singleOrNull() private val enumSerializerFactoryFunc - = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME)).singleOrNull() + = compilerContext.enumSerializerFactoryFunc private val markedEnumSerializerFactoryFunc - = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME)).singleOrNull() + = compilerContext.markedEnumSerializerFactoryFunc fun useFieldMissingOptimization(): Boolean { return throwMissedFieldExceptionFunc != null && throwMissedFieldExceptionArrayFunc != null @@ -535,7 +535,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override } - val serializable = serializerClass?.owner?.let { getSerializableClassDescriptorBySerializer(it) } + val serializable = serializerClass?.owner?.let { compilerContext.getSerializableClassDescriptorBySerializer(it) } requireNotNull(serializerClass) val ctor = if (serializable?.typeParameters?.isNotEmpty() == true) { requireNotNull( 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 d9d5cd1f57f..ea85137e2b7 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 @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlinx.serialization.compiler.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 @@ -61,10 +62,7 @@ internal fun IrClass.findPluginGeneratedMethod(name: String): IrSimpleFunction? } } -internal fun IrClass.isEnumWithLegacyGeneratedSerializer(): Boolean = isInternallySerializableEnum() && useGeneratedEnumSerializer - -internal val IrClass.useGeneratedEnumSerializer: Boolean - get() = true // FIXME This would break if we try to use this new IR compiler with pre-1.4.1 serialization versions. I think we just need to raise min runtime version. +internal fun IrClass.isEnumWithLegacyGeneratedSerializer(context: SerializationPluginContext): Boolean = isInternallySerializableEnum() && !context.runtimeHasEnumSerializerFactoryFunctions internal val IrClass.isSealedSerializableInterface: Boolean get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotation() @@ -100,9 +98,9 @@ internal val IrClass.isSerialInfoAnnotation: Boolean || annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName) || annotations.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) -internal val IrClass.shouldHaveGeneratedSerializer: Boolean - get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) - || isEnumWithLegacyGeneratedSerializer() +internal fun IrClass.shouldHaveGeneratedSerializer(context: SerializationPluginContext): Boolean + = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) + || isEnumWithLegacyGeneratedSerializer(context) internal val IrClass.shouldHaveGeneratedMethodsInCompanion: Boolean get() = this.isSerializableObject || this.isSerializableEnum() || (this.kind == ClassKind.CLASS && hasSerializableOrMetaAnnotation()) || this.isSealedSerializableInterface 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 722557982a5..651d2bb9a6f 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 @@ -56,7 +56,7 @@ open class SerializerIrGenerator( compilerContext: SerializationPluginContext, metadataPlugin: SerializationDescriptorSerializerPlugin?, ) : BaseIrGenerator(irClass, compilerContext) { - protected val serializableIrClass = getSerializableClassDescriptorBySerializer(irClass)!! + protected val serializableIrClass = compilerContext.getSerializableClassDescriptorBySerializer(irClass)!! protected val serialName: String = serializableIrClass.serialName() protected val properties = serializablePropertiesForIrBackend(serializableIrClass, metadataPlugin) @@ -620,13 +620,13 @@ open class SerializerIrGenerator( context: SerializationPluginContext, metadataPlugin: SerializationDescriptorSerializerPlugin?, ) { - val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return + val serializableDesc = context.getSerializableClassDescriptorBySerializer(irClass) ?: return val generator = when { - serializableDesc.isEnumWithLegacyGeneratedSerializer() -> SerializerForEnumsGenerator( + serializableDesc.isEnumWithLegacyGeneratedSerializer(context) -> SerializerForEnumsGenerator( irClass, context ) - serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context) + serializableDesc.isValue -> SerializerForInlineClassGenerator(irClass, context) else -> SerializerIrGenerator(irClass, context, metadataPlugin) } generator.generate() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt index 048f8370b5f..a9fa0bce761 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt @@ -117,7 +117,7 @@ fun findTypeSerializer(context: SerializationPluginContext, type: IrType): IrCla } fun findEnumTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? { val classSymbol = type.classOrNull?.owner ?: return null - return if (classSymbol.kind == ClassKind.ENUM_CLASS && !classSymbol.isEnumWithLegacyGeneratedSerializer()) + return if (classSymbol.kind == ClassKind.ENUM_CLASS && !classSymbol.isEnumWithLegacyGeneratedSerializer(context)) context.referenceClass(enumSerializerId) else null } @@ -130,7 +130,7 @@ internal fun IrClass?.classSerializer(context: SerializationPluginContext): IrCl // can infer @Poly? polymorphicSerializerIfApplicableAutomatically(context)?.let { return it } // default serializable? - if (shouldHaveGeneratedSerializer) { + if (shouldHaveGeneratedSerializer(context)) { // $serializer nested class return this.declarations .filterIsInstance() @@ -217,7 +217,7 @@ fun BaseIrGenerator.allSealedSerializableSubclassesFor( }.unzip() } -internal fun getSerializableClassDescriptorBySerializer(serializer: IrClass): IrClass? { +internal fun SerializationPluginContext.getSerializableClassDescriptorBySerializer(serializer: IrClass): IrClass? { val serializerForClass = serializer.serializerForClass if (serializerForClass != null) return serializerForClass.owner if (serializer.name !in setOf( @@ -226,7 +226,7 @@ internal fun getSerializableClassDescriptorBySerializer(serializer: IrClass): Ir ) ) return null val classDescriptor = (serializer.parent as? IrClass) ?: return null - if (!classDescriptor.shouldHaveGeneratedSerializer) return null + if (!classDescriptor.shouldHaveGeneratedSerializer(this)) return null return classDescriptor } 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 0dae865977c..4ba2a93c8f2 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 @@ -17,9 +17,12 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlinx.serialization.compiler.backend.ir.* import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages import java.util.concurrent.ConcurrentHashMap /** @@ -44,6 +47,22 @@ class SerializationPluginContext(baseContext: IrPluginContext, val metadataPlugi lateinit var serialInfoImplJvmIrGenerator: SerialInfoImplJvmIrGenerator internal val copiedStaticWriteSelf: MutableMap = ConcurrentHashMap() + + internal val enumSerializerFactoryFunc = baseContext.referenceFunctions( + CallableId( + SerializationPackages.internalPackageFqName, + SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME + ) + ).singleOrNull() + + internal val markedEnumSerializerFactoryFunc = baseContext.referenceFunctions( + CallableId( + SerializationPackages.internalPackageFqName, + SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME + ) + ).singleOrNull() + + val runtimeHasEnumSerializerFactoryFunctions = enumSerializerFactoryFunc != null && markedEnumSerializerFactoryFunc != null } private inline fun IrClass.runPluginSafe(block: () -> Unit) { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt index 2c138a023c6..81868dd62c5 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider import org.jetbrains.kotlin.fir.moduleData import org.jetbrains.kotlin.fir.resolve.* +import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.scopes.impl.toConeType import org.jetbrains.kotlin.fir.symbols.SymbolInternals @@ -53,6 +54,21 @@ val kSerializerClassId = ClassId(SerializationPackages.packageFqName, SerialEnti class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGenerationExtension(session) { + internal val runtimeHasEnumSerializerFactory by lazy { + val hasFactory = session.symbolProvider.getTopLevelCallableSymbols( + SerializationPackages.internalPackageFqName, + SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME + ).isNotEmpty() + val hasMarkedFactory = session.symbolProvider.getTopLevelCallableSymbols( + SerializationPackages.internalPackageFqName, + SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME + ).isNotEmpty() + hasFactory && hasMarkedFactory + } + + internal val FirClassSymbol<*>.shouldHaveGeneratedSerializer: Boolean + get() = (isInternalSerializable && isFinalOrOpen()) || (classKind == ClassKind.ENUM_CLASS && hasSerializableAnnotationWithoutArgs && !runtimeHasEnumSerializerFactory) + override fun getNestedClassifiersNames(classSymbol: FirClassSymbol<*>): Set { val result = mutableSetOf() if (classSymbol.shouldHaveGeneratedMethodsInCompanion && !classSymbol.isSerializableObject) @@ -156,7 +172,11 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene return listOf(copy.symbol) } - private fun generateSerializerGetterInCompanion(owner: FirClassSymbol<*>, serializableClassSymbol: FirClassSymbol<*>, callableId: CallableId): FirNamedFunctionSymbol { + private fun generateSerializerGetterInCompanion( + owner: FirClassSymbol<*>, + serializableClassSymbol: FirClassSymbol<*>, + callableId: CallableId + ): FirNamedFunctionSymbol { val f = buildSimpleFunction { moduleData = session.moduleData symbol = FirNamedFunctionSymbol(callableId) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt index faaec7afda1..5ff6e1811cb 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt @@ -5,22 +5,16 @@ package org.jetbrains.kotlinx.serialization.compiler.fir -import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol -import org.jetbrains.kotlin.fir.analysis.checkers.outerClassSymbol import org.jetbrains.kotlin.fir.declarations.findArgumentByName import org.jetbrains.kotlin.fir.declarations.utils.isCompanion -import org.jetbrains.kotlin.fir.declarations.utils.modality import org.jetbrains.kotlin.fir.expressions.classId -import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations -import org.jetbrains.kotlinx.serialization.compiler.resolve.isSerializableObject -import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion internal val FirClassSymbol<*>.hasSerializableAnnotation get() = this.annotations.any { @@ -56,13 +50,7 @@ internal val FirClassSymbol<*>.isInternalSerializable: Boolean internal fun FirClassSymbol<*>.isSerializableEnum(): Boolean = classKind == ClassKind.ENUM_CLASS && hasSerializableAnnotation -internal fun FirClassSymbol<*>.isInternallySerializableEnum(): Boolean = - classKind == ClassKind.ENUM_CLASS && hasSerializableAnnotationWithoutArgs - -internal val FirClassSymbol<*>.shouldHaveGeneratedSerializer: Boolean - get() = (isInternalSerializable && isFinalOrOpen()) || isInternallySerializableEnum() - -private fun FirClassSymbol<*>.isFinalOrOpen(): Boolean { +internal fun FirClassSymbol<*>.isFinalOrOpen(): Boolean { val modality = rawStatus.modality // null means default modality, final return (modality == null || modality == Modality.FINAL || modality == Modality.OPEN) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java index 63ad7828183..92f571b93fe 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java @@ -49,6 +49,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.kt"); } + @Test + @TestMetadata("enums.kt") + public void testEnums() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/enums.kt"); + } + @Test @TestMetadata("multipleProperties.kt") public void testMultipleProperties() throws Exception { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java index ba707c4f942..04b2dd109a7 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java @@ -48,6 +48,12 @@ public class SerializationFirMembersTestGenerated extends AbstractSerializationF runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.kt"); } + @Test + @TestMetadata("enums.kt") + public void testEnums() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/enums.kt"); + } + @Test @TestMetadata("multipleProperties.kt") public void testMultipleProperties() throws Exception { 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 8371568b598..25de06f9087 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 @@ -37,6 +37,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/classSerializerAsObject.kt"); } + @Test + @TestMetadata("enumsAreCached.kt") + public void testEnumsAreCached() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/enumsAreCached.kt"); + } + @Test @TestMetadata("generics.kt") public void testGenerics() throws Exception { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/enumsAreCached.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/enumsAreCached.kt new file mode 100644 index 00000000000..b336e19f93f --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/enumsAreCached.kt @@ -0,0 +1,35 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM_IR + +// WITH_STDLIB + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.internal.* + +enum class Plain { + A, B +} + +@Serializable enum class WithNames { + @SerialName("A") ENTRY1, + @SerialName("B") ENTRY2 +} + +@Serializable +class Holder(val p: Plain, val w: WithNames) + +@OptIn(InternalSerializationApi::class) +fun box(): String { + val cs = (Holder.serializer() as GeneratedSerializer<*>).childSerializers() + val str1 = cs[0].toString() + if (!str1.contains("kotlinx.serialization.internal.EnumSerializer")) return str1 + + /** + * Serialization 1.4.1+ have runtime factories to create EnumSerializer instead of synthetic $serializer, saving bytecode + * and bringing consistency. After updating the version, uncomment this block. + */ +// val str2 = cs[1].toString() +// if (!str2.contains("kotlinx.serialization.internal.EnumSerializer")) return str2 + return "OK" +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/enums.fir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/enums.fir.txt new file mode 100644 index 00000000000..c6816e6c2a1 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/enums.fir.txt @@ -0,0 +1,132 @@ +FILE: enums.kt + public final enum class Plain : R|kotlin/Enum| { + private constructor(): R|Plain| { + super|>() + } + + public final static enum entry A: R|Plain| + public final static enum entry B: R|Plain| + public final static fun values(): R|kotlin/Array| { + } + + public final static fun valueOf(value: R|kotlin/String|): R|Plain| { + } + + public final static val entries: R|kotlin/enums/EnumEntries| + public get(): R|kotlin/enums/EnumEntries| + + } + @R|kotlinx/serialization/Serializable|() public final enum class WithNames : R|kotlin/Enum| { + private constructor(): R|WithNames| { + super|>() + } + + @R|kotlinx/serialization/SerialName|(value = String(A)) public final static enum entry ENTRY1: R|WithNames| = object : R|WithNames| { + private constructor(): R|| { + super() + } + + } + + @R|kotlinx/serialization/SerialName|(value = String(B)) public final static enum entry ENTRY2: R|WithNames| = object : R|WithNames| { + private constructor(): R|| { + super() + } + + } + + public final static fun values(): R|kotlin/Array| { + } + + public final static fun valueOf(value: R|kotlin/String|): R|WithNames| { + } + + public final static val entries: R|kotlin/enums/EnumEntries| + public get(): R|kotlin/enums/EnumEntries| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|WithNames.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|WithNames|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|WithNames| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + private constructor(): R|WithNames.$serializer| + + } + + } + @R|kotlinx/serialization/Serializable|() public final class Holder : R|kotlin/Any| { + public constructor(p: R|Plain|, w: R|WithNames|): R|Holder| { + super() + } + + public final val p: R|Plain| = R|/p| + public get(): R|Plain| + + public final val w: R|WithNames| = R|/w| + public get(): R|WithNames| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|Holder.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|Holder|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|Holder| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + private constructor(): R|Holder.$serializer| + + } + + } + @R|kotlin/OptIn|(markerClass = vararg((Q|kotlinx/serialization/InternalSerializationApi|))) public final fun testSerializers(): R|kotlin/String| { + lval cs: R|kotlin/Array>| = (Q|Holder|.R|/Holder.Companion.serializer|() as R|kotlinx/serialization/internal/GeneratedSerializer<*>|).R|SubstitutionOverride>|>|() + lval str1: R|kotlin/String| = R|/cs|.R|SubstitutionOverride|>|(Int(0)).R|kotlin/Any.toString|() + when () { + R|/str1|.R|kotlin/text/contains|(String(kotlinx.serialization.internal.EnumSerializer)).R|kotlin/Boolean.not|() -> { + ^testSerializers R|/str1| + } + } + + ^testSerializers String(OK) + } + public final fun testSerialization(previous: R|kotlin/String|): R|kotlin/String| { + when () { + !=(R|/previous|, String(OK)) -> { + ^testSerialization R|/previous| + } + } + + lval h: R|Holder| = R|/Holder.Holder|(Q|Plain|.R|/Plain.B|, Q|WithNames|.R|/WithNames.ENTRY1|) + lval s: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|(Q|Holder|.R|/Holder.Companion.serializer|(), R|/h|) + when () { + !=(R|/s|, String({"p":"B","w":"A"})) -> { + ^testSerialization R|/s| + } + } + + ^testSerialization String(OK) + } + public final fun box(): R|kotlin/String| { + ^box R|/testSerialization|(R|/testSerializers|()) + } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/enums.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/enums.kt new file mode 100644 index 00000000000..3c23679fd2b --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/enums.kt @@ -0,0 +1,45 @@ + +// WITH_STDLIB + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.internal.* + +enum class Plain { + A, B +} + +@Serializable enum class WithNames { + @SerialName("A") ENTRY1, + @SerialName("B") ENTRY2 +} + +@Serializable +class Holder(val p: Plain, val w: WithNames) + +@OptIn(InternalSerializationApi::class) +fun testSerializers(): String { + val cs = (Holder.serializer() as GeneratedSerializer<*>).childSerializers() + val str1 = cs[0].toString() + if (!str1.contains("kotlinx.serialization.internal.EnumSerializer")) return str1 + + /** + * Serialization 1.4.1+ have runtime factories to create EnumSerializer instead of synthetic $serializer, saving bytecode + * and bringing consistency. After updating the version, uncomment this block. + */ +// 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 + return "OK" +} + +fun box(): String { + return testSerialization(testSerializers()) +} \ No newline at end of file