diff --git a/core/commonMain/src/kotlinx/serialization/internal/Enums.kt b/core/commonMain/src/kotlinx/serialization/internal/Enums.kt index 1e6201546..7e940769f 100644 --- a/core/commonMain/src/kotlinx/serialization/internal/Enums.kt +++ b/core/commonMain/src/kotlinx/serialization/internal/Enums.kt @@ -7,6 +7,7 @@ package kotlinx.serialization.internal import kotlinx.serialization.* import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* +import kotlin.jvm.Volatile /* * Descriptor used for explicitly serializable enums by the plugin. @@ -49,20 +50,52 @@ internal class EnumDescriptor( } } -// Used for enums that are not explicitly serializable by the plugin +@OptIn(ExperimentalSerializationApi::class) +@InternalSerializationApi +internal fun > createSimpleEnumSerializer(serialName: String, values: Array): KSerializer { + return EnumSerializer(serialName, values) +} + +@OptIn(ExperimentalSerializationApi::class) +@InternalSerializationApi +internal fun > createMarkedEnumSerializer( + serialName: String, + values: Array, + names: Array, + annotations: Array?> +): KSerializer { + val descriptor = EnumDescriptor(serialName, values.size) + values.forEachIndexed { i, v -> + val elementName = names.getOrNull(i) ?: v.name + descriptor.addElement(elementName) + annotations.getOrNull(i)?.forEach { + descriptor.pushAnnotation(it) + } + } + + return EnumSerializer(serialName, values, descriptor) +} + @PublishedApi @OptIn(ExperimentalSerializationApi::class) internal class EnumSerializer>( serialName: String, private val values: Array ) : KSerializer { + private var overriddenDescriptor: SerialDescriptor? = null - override val descriptor: SerialDescriptor = buildSerialDescriptor(serialName, SerialKind.ENUM) { - values.forEach { - val fqn = "$serialName.${it.name}" - val enumMemberDescriptor = buildSerialDescriptor(fqn, StructureKind.OBJECT) - element(it.name, enumMemberDescriptor) - } + internal constructor(serialName: String, values: Array, descriptor: SerialDescriptor) : this(serialName, values) { + overriddenDescriptor = descriptor + } + + override val descriptor: SerialDescriptor by lazy { + overriddenDescriptor ?: createUnmarkedDescriptor(serialName) + } + + private fun createUnmarkedDescriptor(serialName: String): SerialDescriptor { + val d = EnumDescriptor(serialName, values.size) + values.forEach { d.addElement(it.name) } + return d } override fun serialize(encoder: Encoder, value: T) { diff --git a/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt b/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt index 77fa593e9..e1e7d7ff3 100644 --- a/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt +++ b/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt @@ -22,11 +22,36 @@ class CachedSerializersTest { @Serializable abstract class Abstract + @Serializable + enum class SerializableEnum {A, B} + + @SerialInfo + annotation class MyAnnotation(val x: Int) + + @Serializable + enum class SerializableMarkedEnum { + @SerialName("first") + @MyAnnotation(1) + C, + @MyAnnotation(2) + D + } + @Test fun testObjectSerializersAreSame() = noJsLegacy { assertSame(Object.serializer(), Object.serializer()) } + @Test + fun testSerializableEnumSerializersAreSame() = noJsLegacy { + assertSame(SerializableEnum.serializer(), SerializableEnum.serializer()) + } + + @Test + fun testSerializableMarkedEnumSerializersAreSame() = noJsLegacy { + assertSame(SerializableMarkedEnum.serializer(), SerializableMarkedEnum.serializer()) + } + @Test fun testSealedSerializersAreSame() = noJsLegacy { assertSame(SealedParent.serializer(), SealedParent.serializer()) diff --git a/core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt b/core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt index 47433ef7d..8c0f65677 100644 --- a/core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt +++ b/core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt @@ -6,6 +6,7 @@ package kotlinx.serialization import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* +import kotlinx.serialization.internal.EnumSerializer import kotlinx.serialization.test.* import kotlin.test.* @@ -47,9 +48,45 @@ class SerializersLookupEnumTest { @Serializable enum class PlainEnum + @Serializable + enum class SerializableEnum { C, D } + + @Serializable + enum class SerializableMarkedEnum { C, @SerialName("NotD") D } + @Test fun testPlainEnum() { - assertEquals(PlainEnum.serializer(), serializer()) + if (isJsLegacy()) { + assertSame(PlainEnum.serializer()::class, serializer()::class) + } else { + assertSame(PlainEnum.serializer(), serializer()) + } + + if (!isJs()) { + assertIs>(serializer()) + } + } + + @Test + fun testSerializableEnumSerializer() { + assertIs>(SerializableEnum.serializer()) + + if (isJsLegacy()) { + assertSame(SerializableEnum.serializer()::class, serializer()::class) + } else { + assertSame(SerializableEnum.serializer(), serializer()) + } + } + + @Test + fun testSerializableMarkedEnumSerializer() { + assertIs>(SerializableMarkedEnum.serializer()) + + if (isJsLegacy()) { + assertSame(SerializableMarkedEnum.serializer()::class, serializer()::class) + } else { + assertSame(SerializableMarkedEnum.serializer(), serializer()) + } } @Test diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/EnumSerializationTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/EnumSerializationTest.kt index c7350ceec..a57d452b2 100644 --- a/formats/json-tests/commonTest/src/kotlinx/serialization/EnumSerializationTest.kt +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/EnumSerializationTest.kt @@ -127,7 +127,7 @@ class EnumSerializationTest : JsonTestBase() { fun testStructurallyEqualDescriptors() { val libraryGenerated = Wrapper.serializer().descriptor.getElementDescriptor(0) val codeGenerated = MyEnum2.serializer().descriptor - assertNotEquals(libraryGenerated::class, codeGenerated::class) + assertEquals(libraryGenerated::class, codeGenerated::class) libraryGenerated.assertDescriptorEqualsTo(codeGenerated) } }