16
16
*/
17
17
package com.squareup.moshi.kotlin.codegen.ksp
18
18
19
- import com.google.devtools.ksp.processing.Resolver
20
19
import com.google.devtools.ksp.symbol.KSAnnotated
21
20
import com.google.devtools.ksp.symbol.KSAnnotation
22
21
import com.google.devtools.ksp.symbol.KSClassDeclaration
22
+ import com.google.devtools.ksp.symbol.KSDeclaration
23
23
import com.google.devtools.ksp.symbol.KSType
24
24
import com.google.devtools.ksp.symbol.KSValueArgument
25
25
import java.lang.reflect.InvocationHandler
@@ -32,15 +32,6 @@ import kotlin.reflect.KClass
32
32
* Copied experimental utilities from KSP.
33
33
*/
34
34
35
- /* *
36
- * Find a class in the compilation classpath for the given name.
37
- *
38
- * @param name fully qualified name of the class to be loaded; using '.' as separator.
39
- * @return a KSClassDeclaration, or null if not found.
40
- */
41
- internal fun Resolver.getClassDeclarationByName (name : String ): KSClassDeclaration ? =
42
- getClassDeclarationByName(getKSNameFromString(name))
43
-
44
35
internal fun <T : Annotation > KSAnnotated.getAnnotationsByType (annotationKClass : KClass <T >): Sequence <T > {
45
36
return this .annotations.filter {
46
37
it.shortName.getShortName() == annotationKClass.simpleName &&
@@ -74,46 +65,67 @@ private fun KSAnnotation.createInvocationHandler(clazz: Class<*>): InvocationHan
74
65
" $methodName =$value "
75
66
}.toList()
76
67
} else {
77
- val argument = try {
78
- arguments.first { it.name?.asString() == method.name }
79
- } catch (e: NullPointerException ) {
80
- throw IllegalArgumentException (" This is a bug using the default KClass for an annotation" , e)
81
- }
68
+ val argument = arguments.first { it.name?.asString() == method.name }
82
69
when (val result = argument.value ? : method.defaultValue) {
83
70
is Proxy -> result
84
-
85
71
is List <* > -> {
86
- val value = { result.asArray(method) }
72
+ val value = { result.asArray(method, clazz ) }
87
73
cache.getOrPut(Pair (method.returnType, result), value)
88
74
}
89
-
90
75
else -> {
91
76
when {
77
+ // Workaround for java annotation value array type
78
+ // https://github.com/google/ksp/issues/1329
79
+ method.returnType.isArray -> {
80
+ if (result !is Array <* >) {
81
+ val value = { result.asArray(method, clazz) }
82
+ cache.getOrPut(Pair (method.returnType, value), value)
83
+ } else {
84
+ throw IllegalStateException (" unhandled value type, please file a bug at https://github.com/google/ksp/issues/new" )
85
+ }
86
+ }
92
87
method.returnType.isEnum -> {
93
88
val value = { result.asEnum(method.returnType) }
94
89
cache.getOrPut(Pair (method.returnType, result), value)
95
90
}
96
-
97
91
method.returnType.isAnnotation -> {
98
92
val value = { (result as KSAnnotation ).asAnnotation(method.returnType) }
99
93
cache.getOrPut(Pair (method.returnType, result), value)
100
94
}
101
-
102
95
method.returnType.name == " java.lang.Class" -> {
103
- val value = { (result as KSType ).asClass() }
104
- cache.getOrPut(Pair (method.returnType, result), value)
96
+ cache.getOrPut(Pair (method.returnType, result)) {
97
+ when (result) {
98
+ is KSType -> result.asClass(clazz)
99
+ // Handles com.intellij.psi.impl.source.PsiImmediateClassType using reflection
100
+ // since api doesn't contain a reference to this
101
+ else -> Class .forName(
102
+ result.javaClass.methods
103
+ .first { it.name == " getCanonicalText" }
104
+ .invoke(result, false ) as String ,
105
+ )
106
+ }
107
+ }
105
108
}
106
-
107
109
method.returnType.name == " byte" -> {
108
110
val value = { result.asByte() }
109
111
cache.getOrPut(Pair (method.returnType, result), value)
110
112
}
111
-
112
113
method.returnType.name == " short" -> {
113
114
val value = { result.asShort() }
114
115
cache.getOrPut(Pair (method.returnType, result), value)
115
116
}
116
-
117
+ method.returnType.name == " long" -> {
118
+ val value = { result.asLong() }
119
+ cache.getOrPut(Pair (method.returnType, result), value)
120
+ }
121
+ method.returnType.name == " float" -> {
122
+ val value = { result.asFloat() }
123
+ cache.getOrPut(Pair (method.returnType, result), value)
124
+ }
125
+ method.returnType.name == " double" -> {
126
+ val value = { result.asDouble() }
127
+ cache.getOrPut(Pair (method.returnType, result), value)
128
+ }
117
129
else -> result // original value
118
130
}
119
131
}
@@ -134,42 +146,28 @@ private fun KSAnnotation.asAnnotation(
134
146
}
135
147
136
148
@Suppress(" UNCHECKED_CAST" )
137
- private fun List <* >.asArray (method : Method ) =
149
+ private fun List <* >.asArray (method : Method , proxyClass : Class < * > ) =
138
150
when (method.returnType.componentType.name) {
139
151
" boolean" -> (this as List <Boolean >).toBooleanArray()
140
-
141
152
" byte" -> (this as List <Byte >).toByteArray()
142
-
143
153
" short" -> (this as List <Short >).toShortArray()
144
-
145
154
" char" -> (this as List <Char >).toCharArray()
146
-
147
155
" double" -> (this as List <Double >).toDoubleArray()
148
-
149
156
" float" -> (this as List <Float >).toFloatArray()
150
-
151
157
" int" -> (this as List <Int >).toIntArray()
152
-
153
158
" long" -> (this as List <Long >).toLongArray()
154
-
155
- " java.lang.Class" -> (this as List <KSType >).map {
156
- Class .forName(it.declaration.qualifiedName!! .asString())
157
- }.toTypedArray()
158
-
159
+ " java.lang.Class" -> (this as List <KSType >).asClasses(proxyClass).toTypedArray()
159
160
" java.lang.String" -> (this as List <String >).toTypedArray()
160
-
161
161
else -> { // arrays of enums or annotations
162
162
when {
163
163
method.returnType.componentType.isEnum -> {
164
164
this .toArray(method) { result -> result.asEnum(method.returnType.componentType) }
165
165
}
166
-
167
166
method.returnType.componentType.isAnnotation -> {
168
167
this .toArray(method) { result ->
169
168
(result as KSAnnotation ).asAnnotation(method.returnType.componentType)
170
169
}
171
170
}
172
-
173
171
else -> throw IllegalStateException (" Unable to process type ${method.returnType.componentType.name} " )
174
172
}
175
173
}
@@ -192,9 +190,10 @@ private fun <T> Any.asEnum(returnType: Class<T>): T =
192
190
returnType.getDeclaredMethod(" valueOf" , String ::class .java)
193
191
.invoke(
194
192
null ,
195
- // Change from upstream KSP - https://github.com/google/ksp/pull/685
196
193
if (this is KSType ) {
197
194
this .declaration.simpleName.getShortName()
195
+ } else if (this is KSClassDeclaration ) {
196
+ this .simpleName.getShortName()
198
197
} else {
199
198
this .toString()
200
199
},
@@ -204,4 +203,53 @@ private fun Any.asByte(): Byte = if (this is Int) this.toByte() else this as Byt
204
203
205
204
private fun Any.asShort (): Short = if (this is Int ) this .toShort() else this as Short
206
205
207
- private fun KSType.asClass () = Class .forName(this .declaration.qualifiedName!! .asString())
206
+ private fun Any.asLong (): Long = if (this is Int ) this .toLong() else this as Long
207
+
208
+ private fun Any.asFloat (): Float = if (this is Int ) this .toFloat() else this as Float
209
+
210
+ private fun Any.asDouble (): Double = if (this is Int ) this .toDouble() else this as Double
211
+
212
+ // for Class/KClass member
213
+ internal class KSTypeNotPresentException (val ksType : KSType , cause : Throwable ) : RuntimeException(cause)
214
+
215
+ // for Class[]/Array<KClass<*>> member.
216
+ internal class KSTypesNotPresentException (val ksTypes : List <KSType >, cause : Throwable ) : RuntimeException(cause)
217
+
218
+ private fun KSType.asClass (proxyClass : Class <* >) = try {
219
+ Class .forName(this .declaration.toJavaClassName(), true , proxyClass.classLoader)
220
+ } catch (e: Exception ) {
221
+ throw KSTypeNotPresentException (this , e)
222
+ }
223
+
224
+ private fun List<KSType>.asClasses (proxyClass : Class <* >) = try {
225
+ this .map { type -> type.asClass(proxyClass) }
226
+ } catch (e: Exception ) {
227
+ throw KSTypesNotPresentException (this , e)
228
+ }
229
+
230
+ private fun Any.asArray (method : Method , proxyClass : Class <* >) = listOf (this ).asArray(method, proxyClass)
231
+
232
+ private fun KSDeclaration.toJavaClassName (): String {
233
+ val nameDelimiter = ' .'
234
+ val packageNameString = packageName.asString()
235
+ val qualifiedNameString = qualifiedName!! .asString()
236
+ val simpleNames = qualifiedNameString
237
+ .removePrefix(" ${packageNameString}$nameDelimiter " )
238
+ .split(nameDelimiter)
239
+
240
+ return if (simpleNames.size > 1 ) {
241
+ buildString {
242
+ append(packageNameString)
243
+ append(nameDelimiter)
244
+
245
+ simpleNames.forEachIndexed { index, s ->
246
+ if (index > 0 ) {
247
+ append(' $' )
248
+ }
249
+ append(s)
250
+ }
251
+ }
252
+ } else {
253
+ qualifiedNameString
254
+ }
255
+ }
0 commit comments