[Serialization] Reorganize module structure

This commit is contained in:
Dmitriy Novozhilov
2022-08-22 11:49:45 +03:00
parent 0a8cefc8a5
commit cc00dcc038
150 changed files with 493 additions and 322 deletions
@@ -0,0 +1,78 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
// FILE: a.kt
package a
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
object MultiplyingIntSerializer : KSerializer<Int> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("MultiplyingInt", PrimitiveKind.INT)
override fun deserialize(decoder: Decoder): Int {
return decoder.decodeInt() / 2
}
override fun serialize(encoder: Encoder, value: Int) {
encoder.encodeInt(value * 2)
}
}
data class Cont(val i: Int)
object ContSerializer: KSerializer<Cont> {
override fun deserialize(decoder: Decoder): Cont {
return Cont(decoder.decodeInt())
}
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ContSerializer", PrimitiveKind.INT)
override fun serialize(encoder: Encoder, value: Cont) {
encoder.encodeInt(value.i)
}
}
// FILE: test.kt
@file:UseContextualSerialization(Cont::class)
@file:UseSerializers(MultiplyingIntSerializer::class)
package a
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.*
@Serializable
class Holder(
val i: Int,
val c: Cont
)
fun testOnFile(): String {
val j = Json {
serializersModule = SerializersModule {
contextual(ContSerializer)
}
}
val h = Holder(3, Cont(4))
val str = j.encodeToString(
Holder.serializer(),
h
)
if ("""{"i":6,"c":4}""" != str) return str
val decoded = j.decodeFromString(Holder.serializer(), str)
if (decoded.i != h.i) return "i: ${decoded.i}"
if (decoded.c.i != h.c.i) return "c.i: ${decoded.c.i}"
return "OK"
}
fun box(): String {
return testOnFile()
}
@@ -0,0 +1,68 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
package a
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlin.test.assertEquals
class Dummy
class DummyBox<T>
@Serializable(ClassSerializerOnClass::class)
class DummySpecified
class ClassSerializerGeneric : KSerializer<DummyBox<String>> {
override val descriptor get() = PrimitiveSerialDescriptor("ClassSerializerGeneric", PrimitiveKind.INT)
override fun deserialize(decoder: Decoder): DummyBox<String> = TODO()
override fun serialize(encoder: Encoder, value:DummyBox<String>): Unit = TODO()
}
class ClassSerializerDummy : KSerializer<Dummy> {
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<DummyBox<String>> {
override val descriptor get() = PrimitiveSerialDescriptor("ObjectSerializerGeneric", PrimitiveKind.INT)
override fun deserialize(decoder: Decoder): DummyBox<String> = TODO()
override fun serialize(encoder: Encoder, value: DummyBox<String>): Unit = TODO()
}
object ObjectSerializerDummy: KSerializer<Dummy> {
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<DummySpecified> {
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<String>,
@Serializable(ClassSerializerDummy::class) val b: Dummy,
@Serializable(ObjectSerializerGeneric::class) val c: DummyBox<String>,
@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"
}
@@ -0,0 +1,48 @@
// 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 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
if (Json.decodeFromString(Holder.serializer(), s).w != WithNames.ENTRY1) return "Deserialization failure"
return "OK"
}
fun box(): String {
return testSerialization(testSerializers())
}
@@ -0,0 +1,22 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class Foo<T>(val i: Int, val t: T? = null)
@Serializable
class Holder(val f1: Foo<String>, val f2: Foo<Int>)
fun box(): String {
val holder = Holder(Foo(1, "1"), Foo(2))
val str = Json.encodeToString(Holder.serializer(), holder)
if (str != """{"f1":{"i":1,"t":"1"},"f2":{"i":2}}""") return str
val decoded = Json.decodeFromString(Holder.serializer(), str)
if (decoded.f1.t != holder.f1.t) return "f1.t: ${decoded.f1.t}"
return "OK"
}
@@ -0,0 +1,22 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.internal.*
@Serializable
@JvmInline
value class Foo(val i: Int)
@Serializable
class Holder(val f: Foo)
fun box(): String {
if(!Foo.serializer().descriptor.isInline) return "Incorrect descriptor"
val s = Json.encodeToString(Holder.serializer(), Holder(Foo(42)))
if (s != """{"f":42}""") return s
return "OK"
}
@@ -0,0 +1,185 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.json.*
import kotlin.reflect.KClass
import kotlin.test.*
// TODO: for this test to work, runtime dependency should be updated to (yet unreleased) serialization with @MetaSerializable annotation
@MetaSerializable
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class MySerializable
@MetaSerializable
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class MySerializableWithInfo(
val value: Int,
val kclass: KClass<*>
)
@MySerializable
class Project1(val name: String, val language: String)
@MySerializableWithInfo(123, String::class)
class Project2(val name: String, val language: String)
@Serializable
class Wrapper(
@MySerializableWithInfo(234, Int::class) val project: Project2
)
@Serializable
@MySerializableWithInfo(123, String::class)
class Project3(val name: String, val language: String)
@Serializable(with = MySerializer::class)
@MySerializableWithInfo(123, String::class)
class Project4(val name: String, val language: String)
@MySerializableWithInfo(123, String::class)
sealed class TestSealed {
@MySerializableWithInfo(123, String::class)
class A(val value1: String) : TestSealed()
@MySerializableWithInfo(123, String::class)
class B(val value2: String) : TestSealed()
}
@MySerializable
abstract class TestAbstract {
@MySerializableWithInfo(123, String::class)
class A(val value1: String) : TestSealed()
@MySerializableWithInfo(123, String::class)
class B(val value2: String) : TestSealed()
}
@MySerializableWithInfo(123, String::class)
enum class TestEnum { Value1, Value2 }
@MySerializableWithInfo(123, String::class)
object TestObject
object MySerializer : KSerializer<Project4> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Project4", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Project4) = encoder.encodeString("${value.name}:${value.language}")
override fun deserialize(decoder: Decoder): Project4 {
val params = decoder.decodeString().split(':')
return Project4(params[0], params[1])
}
}
fun testMetaSerializable() {
val string = Json.encodeToString(Project1.serializer(), Project1("name", "lang"))
assertEquals("""{"name":"name","language":"lang"}""", string)
val reconstructed = Json.decodeFromString(Project1.serializer(), string)
assertEquals("name", reconstructed.name)
assertEquals("lang", reconstructed.language)
}
fun testMetaSerializableWithInfo() {
val string = Json.encodeToString(Project2.serializer(), Project2("name", "lang"))
assertEquals("""{"name":"name","language":"lang"}""", string)
val reconstructed = Json.decodeFromString(Project2.serializer(), string)
assertEquals("name", reconstructed.name)
assertEquals("lang", reconstructed.language)
val info = Project2.serializer().descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, info.value)
assertEquals(String::class, info.kclass)
}
fun testMetaSerializableOnProperty() {
val info = Wrapper.serializer().descriptor.getElementAnnotations(0).filterIsInstance<MySerializableWithInfo>().first()
assertEquals(234, info.value)
assertEquals(Int::class, info.kclass)
}
fun testSerializableAndMetaAnnotation() {
val string = Json.encodeToString(Project3.serializer(), Project3("name", "lang"))
assertEquals("""{"name":"name","language":"lang"}""", string)
val reconstructed = Json.decodeFromString(Project3.serializer(), string)
assertEquals("name", reconstructed.name)
assertEquals("lang", reconstructed.language)
val info = Project3.serializer().descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, info.value)
assertEquals(String::class, info.kclass)
}
fun testCustomSerializerAndMetaAnnotation() {
val string = Json.encodeToString(Project4.serializer(), Project4("name", "lang"))
assertEquals("""name:lang""", string)
val reconstructed = Json.decodeFromString(Project4.serializer(), string)
assertEquals("name", reconstructed.name)
assertEquals("lang", reconstructed.language)
}
fun testSealed() {
val serializerA = TestSealed.A.serializer()
val serializerB = TestSealed.B.serializer()
assertNotNull(serializerA)
assertNotNull(serializerB)
val infoA = serializerA.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
val infoB = serializerB.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, infoA.value)
assertEquals(String::class, infoA.kclass)
assertEquals(123, infoB.value)
assertEquals(String::class, infoB.kclass)
}
fun testAbstract() {
val serializerA = TestAbstract.A.serializer()
val serializerB = TestAbstract.B.serializer()
assertNotNull(serializerA)
assertNotNull(serializerB)
val infoA = serializerA.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
val infoB = serializerB.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, infoA.value)
assertEquals(String::class, infoA.kclass)
assertEquals(123, infoB.value)
assertEquals(String::class, infoB.kclass)
}
fun testEnum() {
val serializer = TestEnum.serializer()
assertNotNull(serializer)
val info = serializer.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, info.value)
assertEquals(String::class, info.kclass)
}
fun testObject() {
val serializer = TestObject.serializer()
assertNotNull(serializer)
val info = serializer.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, info.value)
assertEquals(String::class, info.kclass)
}
fun box(): String {
testMetaSerializable()
testMetaSerializableWithInfo()
testMetaSerializableOnProperty()
testSealed()
testAbstract()
testEnum()
testObject()
return "OK"
}
@@ -0,0 +1,56 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
// MODULE: lib
// FILE: lib.kt
package a
import kotlinx.serialization.*
@Serializable
open class OpenBody {
var optional: String? = "foo"
}
@Serializable
abstract class AbstractConstructor(var optional: String = "foo")
// MODULE: app(lib)
// FILE: app.kt
package test
import a.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlin.test.assertEquals
@Serializable
class Test1: OpenBody()
@Serializable
class Test2: AbstractConstructor()
fun test1() {
val string = Json.encodeToString(Test1.serializer(), Test1())
assertEquals("{}", string)
val reconstructed = Json.decodeFromString(Test1.serializer(), string)
assertEquals("foo", reconstructed.optional)
}
fun test2() {
val string = Json.encodeToString(Test2.serializer(), Test2())
assertEquals("{}", string)
val reconstructed = Json.decodeFromString(Test2.serializer(), string)
assertEquals("foo", reconstructed.optional)
}
fun box(): String {
test1()
test2()
return "OK"
}
@@ -0,0 +1,37 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
package a
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlin.test.assertEquals
interface I
sealed interface SI
@Serializable
sealed interface SSI
@Serializable
class Holder(
val i: I,
val si: SI,
val ssi: SSI
)
fun SerialDescriptor.checkKind(index: Int, kind: String) {
assertEquals(kind, getElementDescriptor(index).kind.toString())
}
fun box(): String {
val desc = Holder.serializer().descriptor
desc.checkKind(0, "OPEN")
desc.checkKind(1, "OPEN")
desc.checkKind(2, "SEALED")
return "OK"
}