Skip to content

Commit 25acd70

Browse files
committed
Kotlin 2.1 and friends
1 parent 0fd54b7 commit 25acd70

File tree

7 files changed

+124
-94
lines changed

7 files changed

+124
-94
lines changed

gradle/libs.versions.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
autoService = "1.1.1"
33
jdk = "21"
44
jvmTarget = "1.8"
5-
kotlin = "2.0.0"
6-
# No 0.5.0 full release yet due to KSP's 1.0.21 release being busted for CLI/programmatic use
7-
kotlinCompileTesting = "0.5.0-alpha07"
5+
kotlin = "2.1.21"
6+
kotlinCompileTesting = "0.7.1"
87
kotlinpoet = "2.2.0"
9-
ksp = "2.0.21-1.0.25"
8+
ksp = "2.1.21-2.0.1"
109

1110
[plugins]
1211
dokka = { id = "org.jetbrains.dokka", version = "2.0.0" }

moshi-kotlin-codegen/src/main/java/com/squareup/moshi/kotlin/codegen/ksp/MoshiApiUtil.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.squareup.moshi.kotlin.codegen.ksp
1717

18+
import com.google.devtools.ksp.getClassDeclarationByName
1819
import com.google.devtools.ksp.processing.KSPLogger
1920
import com.google.devtools.ksp.processing.Resolver
2021
import com.google.devtools.ksp.symbol.KSClassDeclaration

moshi-kotlin-codegen/src/main/java/com/squareup/moshi/kotlin/codegen/ksp/shadedUtil.kt

Lines changed: 91 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
*/
1717
package com.squareup.moshi.kotlin.codegen.ksp
1818

19-
import com.google.devtools.ksp.processing.Resolver
2019
import com.google.devtools.ksp.symbol.KSAnnotated
2120
import com.google.devtools.ksp.symbol.KSAnnotation
2221
import com.google.devtools.ksp.symbol.KSClassDeclaration
22+
import com.google.devtools.ksp.symbol.KSDeclaration
2323
import com.google.devtools.ksp.symbol.KSType
2424
import com.google.devtools.ksp.symbol.KSValueArgument
2525
import java.lang.reflect.InvocationHandler
@@ -32,15 +32,6 @@ import kotlin.reflect.KClass
3232
* Copied experimental utilities from KSP.
3333
*/
3434

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-
4435
internal fun <T : Annotation> KSAnnotated.getAnnotationsByType(annotationKClass: KClass<T>): Sequence<T> {
4536
return this.annotations.filter {
4637
it.shortName.getShortName() == annotationKClass.simpleName &&
@@ -74,46 +65,67 @@ private fun KSAnnotation.createInvocationHandler(clazz: Class<*>): InvocationHan
7465
"$methodName=$value"
7566
}.toList()
7667
} 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 }
8269
when (val result = argument.value ?: method.defaultValue) {
8370
is Proxy -> result
84-
8571
is List<*> -> {
86-
val value = { result.asArray(method) }
72+
val value = { result.asArray(method, clazz) }
8773
cache.getOrPut(Pair(method.returnType, result), value)
8874
}
89-
9075
else -> {
9176
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+
}
9287
method.returnType.isEnum -> {
9388
val value = { result.asEnum(method.returnType) }
9489
cache.getOrPut(Pair(method.returnType, result), value)
9590
}
96-
9791
method.returnType.isAnnotation -> {
9892
val value = { (result as KSAnnotation).asAnnotation(method.returnType) }
9993
cache.getOrPut(Pair(method.returnType, result), value)
10094
}
101-
10295
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+
}
105108
}
106-
107109
method.returnType.name == "byte" -> {
108110
val value = { result.asByte() }
109111
cache.getOrPut(Pair(method.returnType, result), value)
110112
}
111-
112113
method.returnType.name == "short" -> {
113114
val value = { result.asShort() }
114115
cache.getOrPut(Pair(method.returnType, result), value)
115116
}
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+
}
117129
else -> result // original value
118130
}
119131
}
@@ -134,42 +146,28 @@ private fun KSAnnotation.asAnnotation(
134146
}
135147

136148
@Suppress("UNCHECKED_CAST")
137-
private fun List<*>.asArray(method: Method) =
149+
private fun List<*>.asArray(method: Method, proxyClass: Class<*>) =
138150
when (method.returnType.componentType.name) {
139151
"boolean" -> (this as List<Boolean>).toBooleanArray()
140-
141152
"byte" -> (this as List<Byte>).toByteArray()
142-
143153
"short" -> (this as List<Short>).toShortArray()
144-
145154
"char" -> (this as List<Char>).toCharArray()
146-
147155
"double" -> (this as List<Double>).toDoubleArray()
148-
149156
"float" -> (this as List<Float>).toFloatArray()
150-
151157
"int" -> (this as List<Int>).toIntArray()
152-
153158
"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()
159160
"java.lang.String" -> (this as List<String>).toTypedArray()
160-
161161
else -> { // arrays of enums or annotations
162162
when {
163163
method.returnType.componentType.isEnum -> {
164164
this.toArray(method) { result -> result.asEnum(method.returnType.componentType) }
165165
}
166-
167166
method.returnType.componentType.isAnnotation -> {
168167
this.toArray(method) { result ->
169168
(result as KSAnnotation).asAnnotation(method.returnType.componentType)
170169
}
171170
}
172-
173171
else -> throw IllegalStateException("Unable to process type ${method.returnType.componentType.name}")
174172
}
175173
}
@@ -192,9 +190,10 @@ private fun <T> Any.asEnum(returnType: Class<T>): T =
192190
returnType.getDeclaredMethod("valueOf", String::class.java)
193191
.invoke(
194192
null,
195-
// Change from upstream KSP - https://github.com/google/ksp/pull/685
196193
if (this is KSType) {
197194
this.declaration.simpleName.getShortName()
195+
} else if (this is KSClassDeclaration) {
196+
this.simpleName.getShortName()
198197
} else {
199198
this.toString()
200199
},
@@ -204,4 +203,53 @@ private fun Any.asByte(): Byte = if (this is Int) this.toByte() else this as Byt
204203

205204
private fun Any.asShort(): Short = if (this is Int) this.toShort() else this as Short
206205

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+
}

moshi-kotlin-tests/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ tasks.withType<Test>().configureEach {
3535
tasks.withType<KotlinCompile>().configureEach {
3636
compilerOptions {
3737
allWarningsAsErrors.set(true)
38-
freeCompilerArgs.add(
38+
freeCompilerArgs.addAll(
3939
"-opt-in=kotlin.ExperimentalStdlibApi",
40+
"-Xannotation-default-target=param-property",
4041
)
4142
}
4243
}

moshi-kotlin-tests/codegen-only/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ tasks.withType<Test>().configureEach {
3636
tasks.withType<KotlinCompile>().configureEach {
3737
compilerOptions {
3838
allWarningsAsErrors.set(true)
39-
freeCompilerArgs.add(
39+
freeCompilerArgs.addAll(
4040
"-opt-in=kotlin.ExperimentalStdlibApi",
41+
"-Xannotation-default-target=param-property",
4142
)
4243
}
4344
}

moshi/build.gradle.kts

Lines changed: 24 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import com.vanniktech.maven.publish.KotlinJvm
33
import com.vanniktech.maven.publish.MavenPublishBaseExtension
44
import org.gradle.jvm.tasks.Jar
55
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
6-
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
6+
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions
7+
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
78
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
89

910
plugins {
@@ -12,49 +13,29 @@ plugins {
1213
id("org.jetbrains.dokka")
1314
}
1415

15-
val mainSourceSet by sourceSets.named("main")
16-
val java16: SourceSet by sourceSets.creating {
17-
java {
18-
srcDir("src/main/java16")
19-
}
20-
}
21-
22-
// We use newer JDKs but target 16 for maximum compatibility
23-
val service = project.extensions.getByType<JavaToolchainService>()
24-
val customLauncher =
25-
service.launcherFor {
26-
languageVersion.set(libs.versions.jdk.map(JavaLanguageVersion::of))
27-
}
28-
29-
tasks.named<JavaCompile>("compileJava16Java") {
30-
options.release.set(16)
31-
}
32-
33-
tasks.named<KotlinCompile>("compileJava16Kotlin") {
34-
kotlinJavaToolchain.toolchain.use(customLauncher)
35-
compilerOptions.jvmTarget.set(JvmTarget.JVM_16)
36-
}
37-
38-
// Grant our java16 sources access to internal APIs in the main source set
39-
kotlin.target.compilations.run {
40-
getByName("java16")
41-
.associateWith(getByName(KotlinCompilation.MAIN_COMPILATION_NAME))
42-
}
43-
44-
// Package our actual RecordJsonAdapter from java16 sources in and denote it as an MRJAR
45-
tasks.named<Jar>("jar") {
46-
from(java16.output) {
47-
into("META-INF/versions/16")
48-
}
49-
manifest {
50-
attributes("Multi-Release" to "true")
51-
}
52-
}
16+
kotlin.target {
17+
val main = compilations.getByName(MAIN_COMPILATION_NAME)
18+
val java16 =
19+
compilations.create("java16") {
20+
associateWith(main)
21+
defaultSourceSet.kotlin.srcDir("src/main/java16")
22+
compileJavaTaskProvider.configure {
23+
options.release = 16
24+
}
25+
compileTaskProvider.configure {
26+
(compilerOptions as KotlinJvmCompilerOptions).jvmTarget = JvmTarget.JVM_16
27+
}
28+
}
5329

54-
configurations {
55-
"java16Implementation" {
56-
extendsFrom(api.get())
57-
extendsFrom(implementation.get())
30+
// Package our actual RecordJsonAdapter from java16 sources in and denote it as an MRJAR
31+
tasks.named<Jar>(artifactsTaskName) {
32+
from(java16.output) {
33+
into("META-INF/versions/16")
34+
exclude("META-INF")
35+
}
36+
manifest {
37+
attributes("Multi-Release" to "true")
38+
}
5839
}
5940
}
6041

@@ -78,8 +59,6 @@ tasks
7859
}
7960

8061
dependencies {
81-
// So the j16 source set can "see" main Moshi sources
82-
"java16Implementation"(mainSourceSet.output)
8362
compileOnly(libs.jsr305)
8463
api(libs.okio)
8564

moshi/gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
kotlin.build.archivesTaskOutputAsFriendModule=false

0 commit comments

Comments
 (0)