diff --git a/adapters/build.gradle.kts b/adapters/build.gradle.kts index d78ea6f..d3e0d4d 100644 --- a/adapters/build.gradle.kts +++ b/adapters/build.gradle.kts @@ -14,12 +14,20 @@ * limitations under the License. */ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + plugins { kotlin("jvm") id("com.vanniktech.maven.publish") id("ru.vyarus.animalsniffer") } +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "1.6" + } +} + dependencies { compileOnly(Dependencies.jsr305) api(project(":moshi")) diff --git a/build.gradle.kts b/build.gradle.kts index 413d0ad..1e89334 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,8 +31,8 @@ buildscript { plugins { id("com.vanniktech.maven.publish") version "0.14.2" apply false - id("org.jetbrains.dokka") version "1.4.30" apply false - id("com.diffplug.spotless") version "5.11.0" + id("org.jetbrains.dokka") version "1.4.32" apply false + id("com.diffplug.spotless") version "5.12.4" id("ru.vyarus.animalsniffer") version "1.5.3" apply false id("me.champeau.gradle.japicmp") version "0.2.9" apply false } diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 0700162..fae33df 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -18,7 +18,7 @@ object Dependencies { const val asm = "org.ow2.asm:asm:7.1" const val jsr305 = "com.google.code.findbugs:jsr305:3.0.2" - const val ktlintVersion = "0.39.0" + const val ktlintVersion = "0.41.0" const val okio = "com.squareup.okio:okio:2.10.0" object AnimalSniffer { @@ -39,8 +39,8 @@ object Dependencies { } object Kotlin { - const val version = "1.4.32" - const val metadata = "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.2.0" + const val version = "1.5.0" + const val metadata = "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.3.0" } object KotlinPoet { @@ -53,7 +53,7 @@ object Dependencies { object Testing { const val assertj = "org.assertj:assertj-core:3.11.1" - const val compileTesting = "com.github.tschuchortdev:kotlin-compile-testing:1.3.6" + const val compileTesting = "com.github.tschuchortdev:kotlin-compile-testing:1.4.0" const val junit = "junit:junit:4.13.2" const val truth = "com.google.truth:truth:1.0.1" } diff --git a/kotlin/codegen/build.gradle.kts b/kotlin/codegen/build.gradle.kts index 1311a99..f9e95ae 100644 --- a/kotlin/codegen/build.gradle.kts +++ b/kotlin/codegen/build.gradle.kts @@ -22,7 +22,7 @@ plugins { kotlin("jvm") kotlin("kapt") id("com.vanniktech.maven.publish") - id("com.github.johnrengelman.shadow") version "6.0.0" + id("com.github.johnrengelman.shadow") version "7.0.0" } tasks.withType().configureEach { @@ -102,6 +102,6 @@ val shadowJar = tasks.shadowJar.apply { } artifacts { - runtime(shadowJar) + runtimeOnly(shadowJar) archives(shadowJar) } diff --git a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/DelegateKey.kt b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/DelegateKey.kt index 7838526..42549fd 100644 --- a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/DelegateKey.kt +++ b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/DelegateKey.kt @@ -31,6 +31,7 @@ import com.squareup.kotlinpoet.asClassName import com.squareup.kotlinpoet.asTypeName import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Types +import java.util.Locale /** A JsonAdapter that can be used to encode and decode a particular field. */ internal data class DelegateKey( @@ -50,7 +51,7 @@ internal data class DelegateKey( "At${it.typeName.rawType().simpleName}" } val adapterName = nameAllocator.newName( - "${type.toVariableName().decapitalize()}${qualifierNames}Adapter", + "${type.toVariableName().replaceFirstChar { it.lowercase(Locale.US) }}${qualifierNames}Adapter", this ) diff --git a/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt b/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt index d49415d..02744cb 100644 --- a/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt +++ b/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt @@ -188,111 +188,111 @@ internal class KotlinJsonAdapter( public class KotlinJsonAdapterFactory : JsonAdapter.Factory { override fun create(type: Type, annotations: MutableSet, moshi: Moshi): JsonAdapter<*>? { - if (annotations.isNotEmpty()) return null + if (annotations.isNotEmpty()) return null - val rawType = type.rawType - if (rawType.isInterface) return null - if (rawType.isEnum) return null - if (!rawType.isAnnotationPresent(KOTLIN_METADATA)) return null - if (Util.isPlatformType(rawType)) return null - try { - val generatedAdapter = generatedAdapter(moshi, type, rawType) - if (generatedAdapter != null) { - return generatedAdapter - } - } catch (e: RuntimeException) { - if (e.cause !is ClassNotFoundException) { - throw e - } - // Fall back to a reflective adapter when the generated adapter is not found. + val rawType = type.rawType + if (rawType.isInterface) return null + if (rawType.isEnum) return null + if (!rawType.isAnnotationPresent(KOTLIN_METADATA)) return null + if (Util.isPlatformType(rawType)) return null + try { + val generatedAdapter = generatedAdapter(moshi, type, rawType) + if (generatedAdapter != null) { + return generatedAdapter } - - require(!rawType.isLocalClass) { - "Cannot serialize local class or object expression ${rawType.name}" + } catch (e: RuntimeException) { + if (e.cause !is ClassNotFoundException) { + throw e } - val rawTypeKotlin = rawType.kotlin - require(!rawTypeKotlin.isAbstract) { - "Cannot serialize abstract class ${rawType.name}" - } - require(!rawTypeKotlin.isInner) { - "Cannot serialize inner class ${rawType.name}" - } - require(rawTypeKotlin.objectInstance == null) { - "Cannot serialize object declaration ${rawType.name}" - } - require(!rawTypeKotlin.isSealed) { - "Cannot reflectively serialize sealed class ${rawType.name}. Please register an adapter." - } - - val constructor = rawTypeKotlin.primaryConstructor ?: return null - val parametersByName = constructor.parameters.associateBy { it.name } - constructor.isAccessible = true - - val bindingsByName = LinkedHashMap>() - - for (property in rawTypeKotlin.memberProperties) { - val parameter = parametersByName[property.name] - - if (Modifier.isTransient(property.javaField?.modifiers ?: 0)) { - require(parameter == null || parameter.isOptional) { - "No default value for transient constructor $parameter" - } - continue - } - - require(parameter == null || parameter.type == property.returnType) { - "'${property.name}' has a constructor parameter of type ${parameter!!.type} but a property of type ${property.returnType}." - } - - if (property !is KMutableProperty1 && parameter == null) continue - - property.isAccessible = true - val allAnnotations = property.annotations.toMutableList() - var jsonAnnotation = property.findAnnotation() - - if (parameter != null) { - allAnnotations += parameter.annotations - if (jsonAnnotation == null) { - jsonAnnotation = parameter.findAnnotation() - } - } - - val name = jsonAnnotation?.name ?: property.name - val resolvedPropertyType = resolve(type, rawType, property.returnType.javaType) - val adapter = moshi.adapter( - resolvedPropertyType, - Util.jsonAnnotations(allAnnotations.toTypedArray()), - property.name - ) - - @Suppress("UNCHECKED_CAST") - bindingsByName[property.name] = KotlinJsonAdapter.Binding( - name, - jsonAnnotation?.name ?: name, - adapter, - property as KProperty1, - parameter, - parameter?.index ?: -1 - ) - } - - val bindings = ArrayList?>() - - for (parameter in constructor.parameters) { - val binding = bindingsByName.remove(parameter.name) - require(binding != null || parameter.isOptional) { - "No property for required constructor $parameter" - } - bindings += binding - } - - var index = bindings.size - for (bindingByName in bindingsByName) { - bindings += bindingByName.value.copy(propertyIndex = index++) - } - - val nonTransientBindings = bindings.filterNotNull() - val options = JsonReader.Options.of(*nonTransientBindings.map { it.name }.toTypedArray()) - return KotlinJsonAdapter(constructor, bindings, nonTransientBindings, options).nullSafe() + // Fall back to a reflective adapter when the generated adapter is not found. } + + require(!rawType.isLocalClass) { + "Cannot serialize local class or object expression ${rawType.name}" + } + val rawTypeKotlin = rawType.kotlin + require(!rawTypeKotlin.isAbstract) { + "Cannot serialize abstract class ${rawType.name}" + } + require(!rawTypeKotlin.isInner) { + "Cannot serialize inner class ${rawType.name}" + } + require(rawTypeKotlin.objectInstance == null) { + "Cannot serialize object declaration ${rawType.name}" + } + require(!rawTypeKotlin.isSealed) { + "Cannot reflectively serialize sealed class ${rawType.name}. Please register an adapter." + } + + val constructor = rawTypeKotlin.primaryConstructor ?: return null + val parametersByName = constructor.parameters.associateBy { it.name } + constructor.isAccessible = true + + val bindingsByName = LinkedHashMap>() + + for (property in rawTypeKotlin.memberProperties) { + val parameter = parametersByName[property.name] + + if (Modifier.isTransient(property.javaField?.modifiers ?: 0)) { + require(parameter == null || parameter.isOptional) { + "No default value for transient constructor $parameter" + } + continue + } + + require(parameter == null || parameter.type == property.returnType) { + "'${property.name}' has a constructor parameter of type ${parameter!!.type} but a property of type ${property.returnType}." + } + + if (property !is KMutableProperty1 && parameter == null) continue + + property.isAccessible = true + val allAnnotations = property.annotations.toMutableList() + var jsonAnnotation = property.findAnnotation() + + if (parameter != null) { + allAnnotations += parameter.annotations + if (jsonAnnotation == null) { + jsonAnnotation = parameter.findAnnotation() + } + } + + val name = jsonAnnotation?.name ?: property.name + val resolvedPropertyType = resolve(type, rawType, property.returnType.javaType) + val adapter = moshi.adapter( + resolvedPropertyType, + Util.jsonAnnotations(allAnnotations.toTypedArray()), + property.name + ) + + @Suppress("UNCHECKED_CAST") + bindingsByName[property.name] = KotlinJsonAdapter.Binding( + name, + jsonAnnotation?.name ?: name, + adapter, + property as KProperty1, + parameter, + parameter?.index ?: -1 + ) + } + + val bindings = ArrayList?>() + + for (parameter in constructor.parameters) { + val binding = bindingsByName.remove(parameter.name) + require(binding != null || parameter.isOptional) { + "No property for required constructor $parameter" + } + bindings += binding + } + + var index = bindings.size + for (bindingByName in bindingsByName) { + bindings += bindingByName.value.copy(propertyIndex = index++) + } + + val nonTransientBindings = bindings.filterNotNull() + val options = JsonReader.Options.of(*nonTransientBindings.map { it.name }.toTypedArray()) + return KotlinJsonAdapter(constructor, bindings, nonTransientBindings, options).nullSafe() + } } diff --git a/kotlin/tests/build.gradle.kts b/kotlin/tests/build.gradle.kts index dc67c60..11b0ba6 100644 --- a/kotlin/tests/build.gradle.kts +++ b/kotlin/tests/build.gradle.kts @@ -26,8 +26,7 @@ tasks.withType().configureEach { @Suppress("SuspiciousCollectionReassignment") freeCompilerArgs += listOf( "-Werror", - "-Xopt-in=kotlin.ExperimentalStdlibApi", - "-Xinline-classes" + "-Xopt-in=kotlin.ExperimentalStdlibApi" ) } } diff --git a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/DualKotlinTest.kt b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/DualKotlinTest.kt index da0571f..7cb0c35 100644 --- a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/DualKotlinTest.kt +++ b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/DualKotlinTest.kt @@ -285,9 +285,9 @@ class DualKotlinTest(useReflection: Boolean) { } @Test fun inlineClass() { - val adapter = moshi.adapter() + val adapter = moshi.adapter() - val inline = InlineClass(5) + val inline = ValueClass(5) val expectedJson = """{"i":5}""" @@ -300,12 +300,12 @@ class DualKotlinTest(useReflection: Boolean) { } @JsonClass(generateAdapter = true) - data class InlineConsumer(val inline: InlineClass) + data class InlineConsumer(val inline: ValueClass) @Test fun inlineClassConsumer() { val adapter = moshi.adapter() - val consumer = InlineConsumer(InlineClass(23)) + val consumer = InlineConsumer(ValueClass(23)) @Language("JSON") val expectedJson = @@ -622,9 +622,10 @@ typealias GenericTypeAlias = List?>? @JsonClass(generateAdapter = true) data class GenericClass(val value: T) -// Has to be outside since inline classes are only allowed on top level +// Has to be outside since value classes are only allowed on top level +@JvmInline @JsonClass(generateAdapter = true) -inline class InlineClass(val i: Int) +value class ValueClass(val i: Int) typealias A = Int typealias NullableA = A? diff --git a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/GeneratedAdaptersTest.kt b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/GeneratedAdaptersTest.kt index 6a0bd91..406af92 100644 --- a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/GeneratedAdaptersTest.kt +++ b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/GeneratedAdaptersTest.kt @@ -1191,11 +1191,14 @@ class GeneratedAdaptersTest { annotation class Uppercase(val inFrench: Boolean, val onSundays: Boolean = false) class UppercaseJsonAdapter { - @ToJson fun toJson(@Uppercase(inFrench = true) s: String): String { - return s.toUpperCase(Locale.US) + @ToJson + fun toJson(@Uppercase(inFrench = true) s: String): String { + return s.uppercase(Locale.US) } - @FromJson @Uppercase(inFrench = true) fun fromJson(s: String): String { - return s.toLowerCase(Locale.US) + @FromJson + @Uppercase(inFrench = true) + fun fromJson(s: String): String { + return s.lowercase(Locale.US) } } diff --git a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/annotation/UppercaseInAnnotationPackage.kt b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/annotation/UppercaseInAnnotationPackage.kt index 85f417f..17f9934 100644 --- a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/annotation/UppercaseInAnnotationPackage.kt +++ b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/annotation/UppercaseInAnnotationPackage.kt @@ -24,10 +24,13 @@ import java.util.Locale annotation class UppercaseInAnnotationPackage class UppercaseInAnnotationPackageJsonAdapter { - @ToJson fun toJson(@UppercaseInAnnotationPackage s: String): String { - return s.toUpperCase(Locale.US) + @ToJson + fun toJson(@UppercaseInAnnotationPackage s: String): String { + return s.uppercase(Locale.US) } - @FromJson @UppercaseInAnnotationPackage fun fromJson(s: String): String { - return s.toLowerCase(Locale.US) + @FromJson + @UppercaseInAnnotationPackage + fun fromJson(s: String): String { + return s.lowercase(Locale.US) } } diff --git a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt index 5b8edae..f9c9c99 100644 --- a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt +++ b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt @@ -916,11 +916,14 @@ class KotlinJsonAdapterTest { annotation class Uppercase class UppercaseJsonAdapter { - @ToJson fun toJson(@Uppercase s: String): String { - return s.toUpperCase(Locale.US) + @ToJson + fun toJson(@Uppercase s: String): String { + return s.uppercase(Locale.US) } - @FromJson @Uppercase fun fromJson(s: String): String { - return s.toLowerCase(Locale.US) + @FromJson + @Uppercase + fun fromJson(s: String): String { + return s.lowercase(Locale.US) } } diff --git a/moshi/build.gradle.kts b/moshi/build.gradle.kts index 0971821..7a6a925 100644 --- a/moshi/build.gradle.kts +++ b/moshi/build.gradle.kts @@ -23,11 +23,14 @@ plugins { } tasks.withType() - .matching { it.name.contains("test", true) } .configureEach { kotlinOptions { - @Suppress("SuspiciousCollectionReassignment") // It's not suspicious - freeCompilerArgs += listOf("-Xopt-in=kotlin.ExperimentalStdlibApi") + jvmTarget = "1.6" + + if (name.contains("test", true)) { + @Suppress("SuspiciousCollectionReassignment") // It's not suspicious + freeCompilerArgs += listOf("-Xopt-in=kotlin.ExperimentalStdlibApi") + } } }