Invoke defaults constructor in kotlin code gen (#896)

* Add Util#invokeDefaultConstructor

* Add defaultPrimitiveValue

This will be used to initialize required properties for later invocation of the default constructor

* Move isTransient into PropertyGenerator

We will need this in order to know how to invoke constructors even if they have transient parameters

* Add notion of hasLocalIsPresentName to PropertyGenerator

* Switch to using invokeDefaultConstructor for any default property types

* Add code gen versions of default constructor test

* Fix mismatched names

* Use Arrays.copyOf

* Unwrap InvocationTargetException

* Use name allocator

* Rename createMask to createDefaultValuesParametersMask, use it directly

* Opportunistically clean up result variable holder

Only needs to be made if we have non-parameter instances, otherwise we can just return directly

* Fix mask name

* Remove unnecessary mod

* Switch to local lazily-initialized constructor reference

Not working because of some issue in kotlinpoet I don't understand

* Fix named usage

* Clean up debugging dots

* Add proguard/R8 rule for keeping defaults constructor in targets

* Make constructor lookup property private

* Add another defensive dot

* Rework invokeDefaultConstructor to accept vararg args

A little more idiomatic

* Update proguard rules
This commit is contained in:
Zac Sweers
2019-09-08 17:24:25 -04:00
committed by GitHub
parent 711de52ae1
commit 329d0e14b0
7 changed files with 344 additions and 72 deletions

View File

@@ -0,0 +1,91 @@
package com.squareup.moshi.kotlin
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
import com.squareup.moshi.internal.Util
import org.junit.Test
class DefaultConstructorTest {
@Test fun minimal() {
val expected = TestClass("requiredClass")
val args = arrayOf("requiredClass", null, 0, null, 0, 0)
val mask = Util.createDefaultValuesParametersMask(true, false, false, false, false, false)
val constructor = Util.lookupDefaultsConstructor(TestClass::class.java)
val instance = Util.invokeDefaultConstructor(TestClass::class.java, constructor, mask, *args)
check(instance == expected) {
"No match:\nActual : $instance\nExpected: $expected"
}
}
@Test fun allSet() {
val expected = TestClass("requiredClass", "customOptional", 4, "setDynamic", 5, 6)
val args = arrayOf("requiredClass", "customOptional", 4, "setDynamic", 5, 6)
val mask = Util.createDefaultValuesParametersMask(true, true, true, true, true, true)
val constructor = Util.lookupDefaultsConstructor(TestClass::class.java)
val instance = Util.invokeDefaultConstructor(TestClass::class.java, constructor, mask, *args)
check(instance == expected) {
"No match:\nActual : $instance\nExpected: $expected"
}
}
@Test fun customDynamic() {
val expected = TestClass("requiredClass", "customOptional")
val args = arrayOf("requiredClass", "customOptional", 0, null, 0, 0)
val mask = Util.createDefaultValuesParametersMask(true, true, false, false, false, false)
val constructor = Util.lookupDefaultsConstructor(TestClass::class.java)
val instance = Util.invokeDefaultConstructor(TestClass::class.java, constructor, mask, *args)
check(instance == expected) {
"No match:\nActual : $instance\nExpected: $expected"
}
}
@Test fun minimal_codeGen() {
val expected = TestClass("requiredClass")
val json = """{"required":"requiredClass"}"""
val instance = Moshi.Builder().build().adapter<TestClass>(TestClass::class.java)
.fromJson(json)!!
check(instance == expected) {
"No match:\nActual : $instance\nExpected: $expected"
}
}
@Test fun allSet_codeGen() {
val expected = TestClass("requiredClass", "customOptional", 4, "setDynamic", 5, 6)
val json = """{"required":"requiredClass","optional":"customOptional","optional2":4,"dynamicSelfReferenceOptional":"setDynamic","dynamicOptional":5,"dynamicInlineOptional":6}"""
val instance = Moshi.Builder().build().adapter<TestClass>(TestClass::class.java)
.fromJson(json)!!
check(instance == expected) {
"No match:\nActual : $instance\nExpected: $expected"
}
}
@Test fun customDynamic_codeGen() {
val expected = TestClass("requiredClass", "customOptional")
val json = """{"required":"requiredClass","optional":"customOptional"}"""
val instance = Moshi.Builder().build().adapter<TestClass>(TestClass::class.java)
.fromJson(json)!!
check(instance == expected) {
"No match:\nActual : $instance\nExpected: $expected"
}
}
}
@JsonClass(generateAdapter = true)
data class TestClass(
val required: String,
val optional: String = "optional",
val optional2: Int = 2,
val dynamicSelfReferenceOptional: String = required,
val dynamicOptional: Int = createInt(),
val dynamicInlineOptional: Int = createInlineInt()
)
private fun createInt(): Int {
return 3
}
@Suppress("NOTHING_TO_INLINE")
private inline fun createInlineInt(): Int {
return 3
}