1 /*
2  * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.serialization
6 
7 import kotlinx.serialization.builtins.*
8 import kotlinx.serialization.descriptors.*
9 import kotlinx.serialization.encoding.*
10 import kotlinx.serialization.internal.*
11 import kotlinx.serialization.modules.*
12 import kotlin.reflect.*
13 
14 /**
15  * This class provides support for multiplatform polymorphic serialization for interfaces and abstract classes.
16  *
17  * To avoid the most common security pitfalls and reflective lookup (and potential load) of an arbitrary class,
18  * all serializable implementations of any polymorphic type must be [registered][SerializersModuleBuilder.polymorphic]
19  * in advance in the scope of base polymorphic type, efficiently preventing unbounded polymorphic serialization
20  * of an arbitrary type.
21  *
22  * Polymorphic serialization is enabled automatically by default for interfaces and [Serializable] abstract classes.
23  * To enable this feature explicitly on other types, use `@SerializableWith(PolymorphicSerializer::class)`
24  * or [Polymorphic] annotation on the property.
25  *
26  * Usage of the polymorphic serialization can be demonstrated by the following example:
27  * ```
28  * abstract class BaseRequest()
29  * @Serializable
30  * data class RequestA(val id: Int): BaseRequest()
31  * @Serializable
32  * data class RequestB(val s: String): BaseRequest()
33  *
34  * abstract class BaseResponse()
35  * @Serializable
36  * data class ResponseC(val payload: Long): BaseResponse()
37  * @Serializable
38  * data class ResponseD(val payload: ByteArray): BaseResponse()
39  *
40  * @Serializable
41  * data class Message(
42  *     @Polymorphic val request: BaseRequest,
43  *     @Polymorphic val response: BaseResponse
44  * )
45  * ```
46  * In this example, both request and response in `Message` are serializable with [PolymorphicSerializer].
47  *
48  * `BaseRequest` and `BaseResponse` are base classes and they are captured during compile time by the plugin.
49  * Yet [PolymorphicSerializer] for `BaseRequest` should only allow `RequestA` and `RequestB` serializers, and none of the response's serializers.
50  *
51  * This is achieved via special registration function in the module:
52  * ```
53  * val requestAndResponseModule = SerializersModule {
54  *     polymorphic(BaseRequest::class) {
55  *         subclass(RequestA::class)
56  *         subclass(RequestB::class)
57  *     }
58  *     polymorphic(BaseResponse::class) {
59  *         subclass(ResponseC::class)
60  *         subclass(ResponseD::class)
61  *     }
62  * }
63  * ```
64  *
65  * @see SerializersModule
66  * @see SerializersModuleBuilder.polymorphic
67  */
68 @OptIn(ExperimentalSerializationApi::class)
69 public class PolymorphicSerializer<T : Any>(override val baseClass: KClass<T>) : AbstractPolymorphicSerializer<T>() {
70 
71     @PublishedApi // See comment in SealedClassSerializer
72     internal constructor(
73         baseClass: KClass<T>,
74         classAnnotations: Array<Annotation>
75     ) : this(baseClass) {
76         _annotations = classAnnotations.asList()
77     }
78 
79     private var _annotations: List<Annotation> = emptyList()
80 
<lambda>null81     public override val descriptor: SerialDescriptor by lazy(LazyThreadSafetyMode.PUBLICATION) {
82         buildSerialDescriptor("kotlinx.serialization.Polymorphic", PolymorphicKind.OPEN) {
83             element("type", String.serializer().descriptor)
84             element(
85                 "value",
86                 buildSerialDescriptor("kotlinx.serialization.Polymorphic<${baseClass.simpleName}>", SerialKind.CONTEXTUAL)
87             )
88             annotations = _annotations
89         }.withContext(baseClass)
90     }
91 
toStringnull92     override fun toString(): String {
93         return "kotlinx.serialization.PolymorphicSerializer(baseClass: $baseClass)"
94     }
95 }
96 
97 @InternalSerializationApi
findPolymorphicSerializernull98 public fun <T : Any> AbstractPolymorphicSerializer<T>.findPolymorphicSerializer(
99     decoder: CompositeDecoder,
100     klassName: String?
101 ): DeserializationStrategy<T> =
102     findPolymorphicSerializerOrNull(decoder, klassName) ?: throwSubtypeNotRegistered(klassName, baseClass)
103 
104 @InternalSerializationApi
105 public fun <T : Any> AbstractPolymorphicSerializer<T>.findPolymorphicSerializer(
106     encoder: Encoder,
107     value: T
108 ): SerializationStrategy<T> =
109     findPolymorphicSerializerOrNull(encoder, value) ?: throwSubtypeNotRegistered(value::class, baseClass)
110