11package com.squareup.moshi.kotlin.reflect
22
3- import com.squareup.moshi.internal.DEFAULT_CONSTRUCTOR_MARKER
43import java.lang.reflect.Constructor
5- import java.lang.reflect.Executable
64import java.lang.reflect.Method
75import java.lang.reflect.Modifier
86import kotlin.metadata.KmClass
@@ -12,14 +10,11 @@ import kotlin.metadata.isValue
1210import kotlin.metadata.jvm.KotlinClassMetadata
1311import kotlin.metadata.jvm.signature
1412
15- private val DEFAULT_CONSTRUCTOR_SIGNATURE by
16- lazy(LazyThreadSafetyMode .NONE ) { DEFAULT_CONSTRUCTOR_MARKER !! .descriptor }
17-
1813/* *
1914 * Simple facade over KM constructor-ish types, which could be a constructor or a static creator
2015 * method (value classes).
2116 */
22- internal sealed class KmExecutable <T : Executable > {
17+ internal sealed class KmExecutable <T : Any > {
2318 abstract val parameters: List <KtParameter >
2419 abstract val isDefault: Boolean
2520 abstract val needsDefaultMarker: Boolean
@@ -72,56 +67,25 @@ internal sealed class KmExecutable<T : Executable> {
7267 return boxImplMethod to unboxImplMethod
7368 }
7469
75- private fun Executable.defaultsSignature (): String {
76- val suffixDescriptor =
77- when (this ) {
78- is Constructor <* > -> " V"
79- is Method -> returnType.descriptor
80- }
81- val rawPrefix = jvmMethodSignature.removeSuffix(suffixDescriptor).removeSuffix(" )" )
82- val prefix =
83- when (this ) {
84- is Constructor <* > -> {
85- rawPrefix
86- }
87-
88- is Method -> {
89- // Need to add $default to the end of the method name
90- val (name, rest) = rawPrefix.split(" (" , limit = 2 )
91- // ktlint doesn't support multi-dollar prefixes
92- @Suppress(" CanConvertToMultiDollarString" ) " $name \$ default($rest "
93- }
94- }
95- val parameterCount = parameterTypes.size
96- val maskParamsToAdd = (parameterCount + 31 ) / 32
97- val defaultConstructorSignature = buildString {
98- append(prefix)
99- repeat(maskParamsToAdd) { append(" I" ) }
100- append(DEFAULT_CONSTRUCTOR_SIGNATURE )
101- append(' )' )
102- append(suffixDescriptor)
103- }
104- return defaultConstructorSignature
105- }
106-
10770 operator fun invoke (rawType : Class <* >, kmClass : KmClass ): KmExecutable <* >? {
10871 // If this is a value class, the "constructor" will actually be a static creator function
109- val constructorsBySignature =
72+ val constructorsBySignature: Map < String , Invokable > =
11073 if (kmClass.isValue) {
11174 // kmConstructorSignature is something like constructor-impl(I)I
11275 // Need to look up the matching static function
11376 rawType.declaredMethods
11477 .filter { Modifier .isStatic(it.modifiers) }
115- .associateBy { it.jvmMethodSignature }
78+ .associateBy( { it.jvmMethodSignature }, { Invokable . InvokableMethod (it) })
11679 } else {
117- rawType.declaredConstructors.associateBy { it.jvmMethodSignature }
80+ rawType.declaredConstructors.associateBy(
81+ { it.jvmMethodSignature },
82+ { Invokable .InvokableConstructor (it) },
83+ )
11884 }
11985 val kmConstructor = kmClass.constructors.find { ! it.isSecondary } ? : return null
12086 val kmConstructorSignature = kmConstructor.signature?.toString() ? : return null
12187 val jvmConstructor = constructorsBySignature[kmConstructorSignature] ? : return null
12288
123- val parameterAnnotations = jvmConstructor.parameterAnnotations
124- val parameterTypes = jvmConstructor.parameterTypes
12589 val parameters =
12690 kmConstructor.valueParameters.withIndex().map { (index, kmParam) ->
12791 // Check if this parameter's type is a value class
@@ -130,41 +94,45 @@ internal sealed class KmExecutable<T : Executable> {
13094 KtParameter (
13195 kmParam,
13296 index,
133- parameterTypes[index],
134- parameterAnnotations[index].toList(),
97+ jvmConstructor. parameterTypes[index],
98+ jvmConstructor. parameterAnnotations[index].toList(),
13599 parameterValueClassBoxer,
136100 parameterValueClassUnboxer,
137101 )
138102 }
139103 val anyOptional = parameters.any { it.declaresDefaultValue }
140104 val actualConstructor =
141105 if (anyOptional) {
142- val defaultsSignature = jvmConstructor.defaultsSignature()
143- constructorsBySignature[defaultsSignature] ? : return null
106+ constructorsBySignature[jvmConstructor.defaultsSignature()] ? : return null
144107 } else {
145108 jvmConstructor
146109 }
147110
148- actualConstructor.isAccessible = true
111+ actualConstructor.setAccessible()
149112
150113 return if (kmClass.isValue) {
151114 // Things get quirky here. KM will return the primary constructor for the value class
152115 // as a constructor-impl static function, BUT this function always only returns the
153- // underlying
154- // type. What we want is the boxed type, so we need to be able to invoke the constructor and
155- // then be able to pass it on to the box-impl function to get the full instance.
156- // We can't just skip ahead and use only the box-impl function because the constructor is
157- // the only one that handles default values
158-
159- val boxImpl = constructorsBySignature.entries.first { it.key.startsWith(" box-impl" ) }.value
116+ // underlying type. What we want is the boxed type, so we need to be able to invoke the
117+ // constructor and then be able to pass it on to the box-impl function to get the full
118+ // instance. We can't just skip ahead and use only the box-impl function because the
119+ // constructor is the only one that handles default values
120+
121+ val boxImpl =
122+ constructorsBySignature.entries.first { it.key.startsWith(" box-impl" ) }.value
123+ as Invokable .InvokableMethod
160124 KmExecutableFunction (
161- actualConstructor as Method ,
125+ ( actualConstructor as Invokable . InvokableMethod ).method ,
162126 parameters,
163127 anyOptional,
164- boxImpl as Method ,
128+ boxImpl.method ,
165129 )
166130 } else {
167- KmExecutableConstructor (actualConstructor as Constructor <* >, parameters, anyOptional)
131+ KmExecutableConstructor (
132+ (actualConstructor as Invokable .InvokableConstructor ).constructor ,
133+ parameters,
134+ anyOptional,
135+ )
168136 }
169137 }
170138 }
0 commit comments