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 @file:Suppress("UNUSED")
5 
6 package kotlinx.serialization.internal
7 
8 import kotlinx.serialization.*
9 import kotlinx.serialization.descriptors.*
10 import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME
11 
12 /**
13  * Implementation that plugin uses to implement descriptors for auto-generated serializers.
14  */
15 @PublishedApi
16 @OptIn(ExperimentalSerializationApi::class)
17 internal open class PluginGeneratedSerialDescriptor(
18     override val serialName: String,
19     private val generatedSerializer: GeneratedSerializer<*>? = null,
20     final override val elementsCount: Int
21 ) : SerialDescriptor, CachedNames {
22     override val kind: SerialKind get() = StructureKind.CLASS
23     override val annotations: List<Annotation> get() = classAnnotations ?: emptyList()
24 
25     private var added = -1
26     private val names = Array(elementsCount) { "[UNINITIALIZED]" }
27     private val propertiesAnnotations = arrayOfNulls<MutableList<Annotation>?>(elementsCount)
28 
29     // Classes rarely have annotations, so we can save up a bit of allocations here
30     private var classAnnotations: MutableList<Annotation>? = null
31     private val elementsOptionality = BooleanArray(elementsCount)
32     public override val serialNames: Set<String> get() = indices.keys
33 
34     private var indices: Map<String, Int> = emptyMap()
35     // Cache child serializers, they are not cached by the implementation for nullable types
36     private val childSerializers: Array<KSerializer<*>> by lazy(LazyThreadSafetyMode.PUBLICATION) { generatedSerializer?.childSerializers() ?: EMPTY_SERIALIZER_ARRAY }
37 
38     // Lazy because of JS specific initialization order (#789)
39     internal val typeParameterDescriptors: Array<SerialDescriptor> by lazy(LazyThreadSafetyMode.PUBLICATION) {
40         generatedSerializer?.typeParametersSerializers()?.map { it.descriptor }.compactArray()
41     }
42 
43     // Can be without synchronization but Native will likely break due to freezing
44     private val _hashCode: Int by lazy(LazyThreadSafetyMode.PUBLICATION) { hashCodeImpl(typeParameterDescriptors) }
45 
46     public fun addElement(name: String, isOptional: Boolean = false) {
47         names[++added] = name
48         elementsOptionality[added] = isOptional
49         propertiesAnnotations[added] = null
50         if (added == elementsCount - 1) {
51             indices = buildIndices()
52         }
53     }
54 
55     public fun pushAnnotation(annotation: Annotation) {
56         val list = propertiesAnnotations[added].let {
57             if (it == null) {
58                 val result = ArrayList<Annotation>(1)
59                 propertiesAnnotations[added] = result
60                 result
61             } else {
62                 it
63             }
64         }
65         list.add(annotation)
66     }
67 
68     public fun pushClassAnnotation(a: Annotation) {
69         if (classAnnotations == null) {
70             classAnnotations = ArrayList(1)
71         }
72         classAnnotations!!.add(a)
73     }
74 
75     override fun getElementDescriptor(index: Int): SerialDescriptor {
76         return childSerializers.getChecked(index).descriptor
77     }
78 
79     override fun isElementOptional(index: Int): Boolean = elementsOptionality.getChecked(index)
80     override fun getElementAnnotations(index: Int): List<Annotation> =
81         propertiesAnnotations.getChecked(index) ?: emptyList()
82     override fun getElementName(index: Int): String = names.getChecked(index)
83     override fun getElementIndex(name: String): Int = indices[name] ?: UNKNOWN_NAME
84 
85     private fun buildIndices(): Map<String, Int> {
86         val indices = HashMap<String, Int>()
87         for (i in names.indices) {
88             indices[names[i]] = i
89         }
90         return indices
91     }
92 
93     override fun equals(other: Any?): Boolean = equalsImpl(other) { otherDescriptor ->
94         typeParameterDescriptors.contentEquals(otherDescriptor.typeParameterDescriptors)
95     }
96 
97     override fun hashCode(): Int = _hashCode
98 
99     override fun toString(): String {
100         return (0 until elementsCount).joinToString(", ", "$serialName(", ")") { i ->
101             getElementName(i) + ": " + getElementDescriptor(i).serialName
102         }
103     }
104 }
105 
106 @OptIn(ExperimentalSerializationApi::class)
equalsImplnull107 internal inline fun <reified SD : SerialDescriptor> SD.equalsImpl(
108     other: Any?,
109     typeParamsAreEqual: (otherDescriptor: SD) -> Boolean
110 ): Boolean {
111     if (this === other) return true
112     if (other !is SD) return false
113     if (serialName != other.serialName) return false
114     if (!typeParamsAreEqual(other)) return false
115     if (this.elementsCount != other.elementsCount) return false
116     for (index in 0 until elementsCount) {
117         if (getElementDescriptor(index).serialName != other.getElementDescriptor(index).serialName) return false
118         if (getElementDescriptor(index).kind != other.getElementDescriptor(index).kind) return false
119     }
120     return true
121 }
122 
123 @OptIn(ExperimentalSerializationApi::class)
hashCodeImplnull124 internal fun SerialDescriptor.hashCodeImpl(typeParams: Array<SerialDescriptor>): Int {
125     var result = serialName.hashCode()
126     result = 31 * result + typeParams.contentHashCode()
127     val elementDescriptors = elementDescriptors
128     val namesHash = elementDescriptors.elementsHashCodeBy { it.serialName }
129     val kindHash = elementDescriptors.elementsHashCodeBy { it.kind }
130     result = 31 * result + namesHash
131     result = 31 * result + kindHash
132     return result
133 }
134