From 9b9522d71487845397b80b3d737175e27a64ad14 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Tue, 14 Dec 2021 17:53:09 +0000 Subject: [PATCH] Allow defining custom KSerializer for non-generic class as a class Previously only objects were allowed. Due to a bug, this accidentally worked in old backend. #KT-46444 Fixed Fixes https://github.com/Kotlin/kotlinx.serialization/issues/1587 --- .../compiler/backend/ir/GeneratorHelpers.kt | 9 ++- .../SerializationIrBoxTestGenerated.java | 6 ++ .../testData/boxIr/classSerializerAsObject.kt | 68 +++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/classSerializerAsObject.kt 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 a7a67365278..e175e9b1361 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 @@ -1093,7 +1093,14 @@ interface IrBuilderExtension { val typeParameters = ctorDecl.parentAsClass.typeParameters val substitutedReturnType = ctorDecl.returnType.substitute(typeParameters, typeArgs) - return irInvoke(null, ctor, typeArguments = typeArgs, valueArguments = args, returnTypeHint = substitutedReturnType) + return irInvoke( + null, + ctor, + // User may declare serializer with fixed type arguments, e.g. class SomeSerializer : KSerializer> + typeArguments = typeArgs.takeIf { it.size == ctorDecl.typeParameters.size }.orEmpty(), + valueArguments = args.takeIf { it.size == ctorDecl.valueParameters.size }.orEmpty(), + returnTypeHint = substitutedReturnType + ) } fun collectSerialInfoAnnotations(irClass: IrClass): List { 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 a4e6397fa29..1719df71b49 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 @@ -25,6 +25,12 @@ 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("classSerializerAsObject.kt") + public void testClassSerializerAsObject() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/classSerializerAsObject.kt"); + } + @Test @TestMetadata("multimoduleInheritance.kt") public void testMultimoduleInheritance() throws Exception { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/classSerializerAsObject.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/classSerializerAsObject.kt new file mode 100644 index 00000000000..c327273358a --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/classSerializerAsObject.kt @@ -0,0 +1,68 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM_IR + +// WITH_RUNTIME + +package a + +import kotlinx.serialization.* + +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlin.test.assertEquals + +class Dummy + +class DummyBox + +@Serializable(ClassSerializerOnClass::class) +class DummySpecified + +class ClassSerializerGeneric : KSerializer> { + override val descriptor get() = PrimitiveSerialDescriptor("ClassSerializerGeneric", PrimitiveKind.INT) + override fun deserialize(decoder: Decoder): DummyBox = TODO() + override fun serialize(encoder: Encoder, value:DummyBox): Unit = TODO() +} + +class ClassSerializerDummy : KSerializer { + override val descriptor get() = PrimitiveSerialDescriptor("ClassSerializerDummy", PrimitiveKind.INT) + override fun deserialize(decoder: Decoder): Dummy = TODO() + override fun serialize(encoder: Encoder, value: Dummy): Unit = TODO() +} + +object ObjectSerializerGeneric: KSerializer> { + override val descriptor get() = PrimitiveSerialDescriptor("ObjectSerializerGeneric", PrimitiveKind.INT) + override fun deserialize(decoder: Decoder): DummyBox = TODO() + override fun serialize(encoder: Encoder, value: DummyBox): Unit = TODO() +} + +object ObjectSerializerDummy: KSerializer { + override val descriptor get() = PrimitiveSerialDescriptor("ObjectSerializerDummy", PrimitiveKind.INT) + override fun deserialize(decoder: Decoder): Dummy = TODO() + override fun serialize(encoder: Encoder, value:Dummy): Unit = TODO() +} + +class ClassSerializerOnClass: KSerializer { + override val descriptor get() = PrimitiveSerialDescriptor("ClassSerializerOnClass", PrimitiveKind.INT) + override fun deserialize(decoder: Decoder): DummySpecified = TODO() + override fun serialize(encoder: Encoder, value:DummySpecified): Unit = TODO() +} + +@Serializable +class Holder( + @Serializable(ClassSerializerGeneric::class) val a: DummyBox, + @Serializable(ClassSerializerDummy::class) val b: Dummy, + @Serializable(ObjectSerializerGeneric::class) val c: DummyBox, + @Serializable(ObjectSerializerDummy::class) val d: Dummy, + val e: DummySpecified +) + +fun box(): String { + val descs = Holder.serializer().descriptor.elementDescriptors.toList() + assertEquals("ClassSerializerGeneric", descs[0].serialName) + assertEquals("ClassSerializerDummy", descs[1].serialName) + assertEquals("ObjectSerializerGeneric", descs[2].serialName) + assertEquals("ObjectSerializerDummy", descs[3].serialName) + assertEquals("ClassSerializerOnClass", descs[4].serialName) + return "OK" +} \ No newline at end of file