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