1 /*
<lambda>null2  * 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.modules
6 
7 import kotlinx.serialization.*
8 import kotlinx.serialization.internal.*
9 import kotlin.js.*
10 import kotlin.jvm.*
11 import kotlin.reflect.*
12 
13 /**
14  * [SerializersModule] is a collection of serializers used by [ContextualSerializer] and [PolymorphicSerializer]
15  * to override or provide serializers at the runtime, whereas at the compile-time they provided by the serialization plugin.
16  * It can be considered as a map where serializers can be found using their statically known KClasses.
17  *
18  * To enable runtime serializers resolution, one of the special annotations must be used on target types
19  * ([Polymorphic] or [Contextual]), and a serial module with serializers should be used during construction of [SerialFormat].
20  *
21  * Serializers module can be built with `SerializersModule {}` builder function.
22  * Empty module can be obtained with `EmptySerializersModule()` factory function.
23  *
24  * @see Contextual
25  * @see Polymorphic
26  */
27 public sealed class SerializersModule {
28 
29     @ExperimentalSerializationApi
30     @Deprecated(
31         "Deprecated in favor of overload with default parameter",
32         ReplaceWith("getContextual(kclass)"),
33         DeprecationLevel.HIDDEN
34     ) // Was experimental since 1.0.0, HIDDEN in 1.2.0 in a backwards-compatible manner
35     public fun <T : Any> getContextual(kclass: KClass<T>): KSerializer<T>? =
36         getContextual(kclass, emptyList())
37 
38     /**
39      * Returns a contextual serializer associated with a given [kClass].
40      * If given class has generic parameters and module has provider for [kClass],
41      * [typeArgumentsSerializers] are used to create serializer.
42      * This method is used in context-sensitive operations on a property marked with [Contextual] by a [ContextualSerializer].
43      *
44      * @see SerializersModuleBuilder.contextual
45      */
46     @ExperimentalSerializationApi
47     public abstract fun <T : Any> getContextual(
48         kClass: KClass<T>,
49         typeArgumentsSerializers: List<KSerializer<*>> = emptyList()
50     ): KSerializer<T>?
51 
52     /**
53      * Returns a polymorphic serializer registered for a class of the given [value] in the scope of [baseClass].
54      */
55     @ExperimentalSerializationApi
56     public abstract fun <T : Any> getPolymorphic(baseClass: KClass<in T>, value: T): SerializationStrategy<T>?
57 
58     /**
59      * Returns a polymorphic deserializer registered for a [serializedClassName] in the scope of [baseClass]
60      * or default value constructed from [serializedClassName] if a default serializer provider was registered.
61      */
62     @ExperimentalSerializationApi
63     public abstract fun <T : Any> getPolymorphic(baseClass: KClass<in T>, serializedClassName: String?): DeserializationStrategy<T>?
64 
65     /**
66      * Copies contents of this module to the given [collector].
67      */
68     @ExperimentalSerializationApi
69     public abstract fun dumpTo(collector: SerializersModuleCollector)
70 }
71 
72 /**
73  * A [SerializersModule] which is empty and always returns `null`.
74  */
75 @Deprecated("Deprecated in the favour of 'EmptySerializersModule()'",
76     level = DeprecationLevel.WARNING,
77     replaceWith = ReplaceWith("EmptySerializersModule()"))
78 @JsName("EmptySerializersModuleLegacyJs") // Compatibility with JS
79 public val EmptySerializersModule: SerializersModule = SerialModuleImpl(emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())
80 
81 /**
82  * Returns a combination of two serial modules
83  *
84  * If serializer for some class presents in both modules, a [SerializerAlreadyRegisteredException] is thrown.
85  * To overwrite serializers, use [SerializersModule.overwriteWith] function.
86  */
<lambda>null87 public operator fun SerializersModule.plus(other: SerializersModule): SerializersModule = SerializersModule {
88     include(this@plus)
89     include(other)
90 }
91 
92 /**
93  * Returns a combination of two serial modules
94  *
95  * If serializer for some class presents in both modules, result module
96  * will contain serializer from [other] module.
97  */
98 @OptIn(ExperimentalSerializationApi::class)
<lambda>null99 public infix fun SerializersModule.overwriteWith(other: SerializersModule): SerializersModule = SerializersModule {
100     include(this@overwriteWith)
101     other.dumpTo(object : SerializersModuleCollector {
102         override fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>) {
103             registerSerializer(kClass, ContextualProvider.Argless(serializer), allowOverwrite = true)
104         }
105 
106         override fun <T : Any> contextual(
107             kClass: KClass<T>,
108             provider: (serializers: List<KSerializer<*>>) -> KSerializer<*>
109         ) {
110             registerSerializer(kClass, ContextualProvider.WithTypeArguments(provider), allowOverwrite = true)
111         }
112 
113         override fun <Base : Any, Sub : Base> polymorphic(
114             baseClass: KClass<Base>,
115             actualClass: KClass<Sub>,
116             actualSerializer: KSerializer<Sub>
117         ) {
118             registerPolymorphicSerializer(baseClass, actualClass, actualSerializer, allowOverwrite = true)
119         }
120 
121         override fun <Base : Any> polymorphicDefaultSerializer(
122             baseClass: KClass<Base>,
123             defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?
124         ) {
125             registerDefaultPolymorphicSerializer(baseClass, defaultSerializerProvider, allowOverwrite = true)
126         }
127 
128         override fun <Base : Any> polymorphicDefaultDeserializer(
129             baseClass: KClass<Base>,
130             defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<Base>?
131         ) {
132             registerDefaultPolymorphicDeserializer(baseClass, defaultDeserializerProvider, allowOverwrite = true)
133         }
134     })
135 }
136 
137 // Implementation details below
138 
139 /**
140  * A default implementation of [SerializersModule]
141  * which uses hash maps to store serializers associated with KClasses.
142  */
143 @Suppress("UNCHECKED_CAST")
144 @OptIn(ExperimentalSerializationApi::class)
145 internal class SerialModuleImpl(
146     private val class2ContextualFactory: Map<KClass<*>, ContextualProvider>,
147     @JvmField val polyBase2Serializers: Map<KClass<*>, Map<KClass<*>, KSerializer<*>>>,
148     private val polyBase2DefaultSerializerProvider: Map<KClass<*>, PolymorphicSerializerProvider<*>>,
149     private val polyBase2NamedSerializers: Map<KClass<*>, Map<String, KSerializer<*>>>,
150     private val polyBase2DefaultDeserializerProvider: Map<KClass<*>, PolymorphicDeserializerProvider<*>>
151 ) : SerializersModule() {
152 
getPolymorphicnull153     override fun <T : Any> getPolymorphic(baseClass: KClass<in T>, value: T): SerializationStrategy<T>? {
154         if (!baseClass.isInstance(value)) return null
155         // Registered
156         val registered = polyBase2Serializers[baseClass]?.get(value::class) as? SerializationStrategy<T>
157         if (registered != null) return registered
158         // Default
159         return (polyBase2DefaultSerializerProvider[baseClass] as? PolymorphicSerializerProvider<T>)?.invoke(value)
160     }
161 
getPolymorphicnull162     override fun <T : Any> getPolymorphic(baseClass: KClass<in T>, serializedClassName: String?): DeserializationStrategy<T>? {
163         // Registered
164         val registered = polyBase2NamedSerializers[baseClass]?.get(serializedClassName) as? KSerializer<out T>
165         if (registered != null) return registered
166         // Default
167         return (polyBase2DefaultDeserializerProvider[baseClass] as? PolymorphicDeserializerProvider<T>)?.invoke(serializedClassName)
168     }
169 
getContextualnull170     override fun <T : Any> getContextual(kClass: KClass<T>, typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<T>? {
171         return (class2ContextualFactory[kClass]?.invoke(typeArgumentsSerializers)) as? KSerializer<T>?
172     }
173 
dumpTonull174     override fun dumpTo(collector: SerializersModuleCollector) {
175         class2ContextualFactory.forEach { (kclass, serial) ->
176             when (serial) {
177                 is ContextualProvider.Argless -> collector.contextual(
178                     kclass as KClass<Any>,
179                     serial.serializer as KSerializer<Any>
180                 )
181                 is ContextualProvider.WithTypeArguments -> collector.contextual(kclass, serial.provider)
182             }
183         }
184 
185         polyBase2Serializers.forEach { (baseClass, classMap) ->
186             classMap.forEach { (actualClass, serializer) ->
187                 collector.polymorphic(
188                     baseClass as KClass<Any>,
189                     actualClass as KClass<Any>,
190                     serializer.cast()
191                 )
192             }
193         }
194 
195         polyBase2DefaultSerializerProvider.forEach { (baseClass, provider) ->
196             collector.polymorphicDefaultSerializer(baseClass as KClass<Any>, provider as (PolymorphicSerializerProvider<Any>))
197         }
198 
199         polyBase2DefaultDeserializerProvider.forEach { (baseClass, provider) ->
200             collector.polymorphicDefaultDeserializer(baseClass as KClass<Any>, provider as (PolymorphicDeserializerProvider<out Any>))
201         }
202     }
203 }
204 
205 internal typealias PolymorphicDeserializerProvider<Base> = (className: String?) -> DeserializationStrategy<Base>?
206 internal typealias PolymorphicSerializerProvider<Base> = (value: Base) -> SerializationStrategy<Base>?
207 
208 /** This class is needed to support re-registering the same static (argless) serializers:
209  *
210  * ```
211  * val m1 = serializersModuleOf(A::class, A.serializer())
212  * val m2 = serializersModuleOf(A::class, A.serializer())
213  * val aggregate = m1 + m2 // should not throw
214  * ```
215  */
216 internal sealed class ContextualProvider {
invokenull217     abstract operator fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*>
218 
219     class Argless(val serializer: KSerializer<*>) : ContextualProvider() {
220         override fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*> = serializer
221 
222         override fun equals(other: Any?): Boolean = other is Argless && other.serializer == this.serializer
223 
224         override fun hashCode(): Int = serializer.hashCode()
225     }
226 
227     class WithTypeArguments(val provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>) :
228         ContextualProvider() {
invokenull229         override fun invoke(typeArgumentsSerializers: List<KSerializer<*>>): KSerializer<*> =
230             provider(typeArgumentsSerializers)
231     }
232 
233 }
234