From 77462850f3b828fd8b131755f6627878054d0b21 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 10 Jan 2022 12:39:04 +0900 Subject: [PATCH 01/13] fix pom for add Java source --- pom.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b41d8635..ce5154ca 100644 --- a/pom.xml +++ b/pom.xml @@ -124,7 +124,6 @@ - ${project.basedir}/src/main/kotlin ${project.basedir}/src/test/kotlin @@ -139,6 +138,13 @@ compile + + + ${project.basedir}/target/generated-sources + ${project.basedir}/src/main/java + ${project.basedir}/src/main/kotlin + + From 35fd0e7d125f4f0bfeb7cd63ebb54ee672d79def Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 10 Jan 2022 12:39:22 +0900 Subject: [PATCH 02/13] add SpreadWrapper --- .../jackson/module/kotlin/SpreadWrapper.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/com/fasterxml/jackson/module/kotlin/SpreadWrapper.java diff --git a/src/main/java/com/fasterxml/jackson/module/kotlin/SpreadWrapper.java b/src/main/java/com/fasterxml/jackson/module/kotlin/SpreadWrapper.java new file mode 100644 index 00000000..eafe17e7 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/kotlin/SpreadWrapper.java @@ -0,0 +1,16 @@ +package com.fasterxml.jackson.module.kotlin; + +import kotlin.reflect.KFunction; +import org.jetbrains.annotations.NotNull; + +/** + * Wrapper to avoid costly calls using spread operator. + * @since 2.13 + */ +class SpreadWrapper { + private SpreadWrapper() {} + + static T call(@NotNull KFunction function, @NotNull Object[] args) { + return function.call(args); + } +} From b2b0058a195945340f906dff7c4573ba2fa46804 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 10 Jan 2022 18:33:47 +0900 Subject: [PATCH 03/13] add ArgumentBucket and generator --- .../jackson/module/kotlin/ArgumentBucket.kt | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt new file mode 100644 index 00000000..13ca3a9e --- /dev/null +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt @@ -0,0 +1,151 @@ +package com.fasterxml.jackson.module.kotlin + +import kotlin.reflect.KParameter + +/** + * Calculation of where the initialization state of KParameter.index is recorded in the masks. + * @return index / 32(= Int.SIZE_BITS) + */ +private fun getMaskIndex(index: Int) = index shr 5 + +/** + * Calculation of where the initialization state of KParameter.index is recorded in the bit of int. + * @return index % 32(= Int.SIZE_BITS) + */ +private fun getFlagIndex(index: Int) = index and 31 + +/** + * Generator for [ArgumentBucket]. + * Refer to the documentation of [ArgumentBucket] and factory function for the contents of each argument. + */ +internal class BucketGenerator private constructor( + private val paramSize: Int, + private val originalValues: Array, + private val originalMasks: IntArray, + private val originalInitializedCount: Int, + private val parameters: List +) { + fun generate(): ArgumentBucket = ArgumentBucket( + paramSize, + originalValues.clone(), + originalMasks.clone(), + originalInitializedCount, + parameters + ) + + companion object { + // -1 is a value where all bits are filled with 1 + private const val FILLED_MASK: Int = -1 + + // The maximum size of the array is obtained by getMaskIndex(paramSize) + 1. + private fun getOriginalMasks(paramSize: Int): IntArray = IntArray(getMaskIndex(paramSize) + 1) { FILLED_MASK } + + /** + * @return [BucketGenerator] when the target of the call is a constructor. + */ + fun forConstructor(parameters: List): BucketGenerator { + val paramSize = parameters.size + // Since the constructor does not require any instance parameters, do not operation the values. + return BucketGenerator(paramSize, Array(paramSize) { null }, getOriginalMasks(paramSize), 0, parameters) + } + + /** + * @return [BucketGenerator] when the target of the call is a method. + */ + fun forMethod(parameters: List, instance: Any): BucketGenerator { + val paramSize = parameters.size + + // Since the method requires instance parameter, it is necessary to perform several operations. + + // In the jackson-module-kotlin process, instance parameters are always at the top, + // so they should be placed at the top of originalValues. + val originalValues = Array(paramSize) { null }.apply { this[0] = instance } + // Since the instance parameters have already been initialized, + // the originalMasks must also be in the corresponding state. + val originalMasks = getOriginalMasks(paramSize).apply { this[0] = this[0] and 1.inv() } + // Since the instance parameters have already been initialized, the originalInitializedCount will be 1. + return BucketGenerator(paramSize, originalValues, originalMasks, 1, parameters) + } + } +} + +/** + * Class for managing arguments and their initialization state. + * [masks] is used to manage the initialization state of arguments. + * For the [masks] bit, 0 means initialized and 1 means uninitialized. + * + * At this point, this management method may not necessarily be ideal, + * but the reason that using this method is to simplify changes like @see #439. + * + * @property paramSize Cache of [parameters].size. + * @property actualValues Arguments arranged in order in the manner of a bucket sort. + * @property masks Initialization state of arguments. + * @property initializedCount Number of initialized parameters. + * @property parameters Parameters of the KFunction to be called. + */ +internal class ArgumentBucket( + private val paramSize: Int, + val actualValues: Array, + private val masks: IntArray, + private var initializedCount: Int, + private val parameters: List +): Map { + class Entry internal constructor( + override val key: KParameter, + override var value: Any? + ) : Map.Entry + + /** + * If the argument corresponding to KParameter.index is initialized, true is returned. + */ + private fun isInitialized(index: Int): Boolean = masks[getMaskIndex(index)] + .let { (it and BIT_FLAGS[getFlagIndex(index)]) == it } + + override val entries: Set> + get() = parameters.fold(HashSet()) { acc, cur -> + val index = cur.index + acc.apply { if (isInitialized(index)) add(Entry(parameters[index], actualValues[index])) } + } + override val keys: Set + get() = parameters.fold(HashSet()) { acc, cur -> acc.apply { if (isInitialized(cur.index)) add(cur) } } + override val size: Int + get() = initializedCount + override val values: Collection + get() = values.filterIndexed { index, _ -> isInitialized(index) } + + override fun containsKey(key: KParameter): Boolean = isInitialized(key.index) + + override fun containsValue(value: Any?): Boolean = + (0 until paramSize).any { isInitialized(it) && value == actualValues[it] } + + override fun get(key: KParameter): Any? = actualValues[key.index] + + override fun isEmpty(): Boolean = initializedCount == 0 + + /** + * Set the value to KParameter.index. + * However, if the corresponding index has already been initialized, nothing is done. + */ + operator fun set(index: Int, value: Any?) { + val maskIndex = getMaskIndex(index) + val flagIndex = getFlagIndex(index) + + val updatedMask = masks[maskIndex] and BIT_FLAGS[flagIndex] + + if (updatedMask != masks[maskIndex]) { + actualValues[index] = value + masks[maskIndex] = updatedMask + initializedCount++ + } + } + + /** + * Return true if all arguments are [set]. + */ + fun isFullInitialized(): Boolean = initializedCount == paramSize + + companion object { + // List of Int with only 1 bit enabled. + private val BIT_FLAGS: List = IntArray(Int.SIZE_BITS) { (1 shl it).inv() }.asList() + } +} From 25ebb1c1bb8815f81af500f727541c65008e6500 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 10 Jan 2022 18:35:05 +0900 Subject: [PATCH 04/13] fix creator --- .../module/kotlin/ConstructorValueCreator.kt | 1 + .../jackson/module/kotlin/MethodValueCreator.kt | 2 ++ .../jackson/module/kotlin/ValueCreator.kt | 15 ++++++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt index 4f711679..eb6e26c2 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt @@ -5,6 +5,7 @@ import kotlin.reflect.jvm.isAccessible internal class ConstructorValueCreator(override val callable: KFunction) : ValueCreator() { override val accessible: Boolean = callable.isAccessible + override val bucketGenerator: BucketGenerator = BucketGenerator.forConstructor(callable.parameters) init { // To prevent the call from failing, save the initial value and then rewrite the flag. diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt index 96f489a1..e3891ca9 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt @@ -12,6 +12,8 @@ internal class MethodValueCreator private constructor( val companionObjectInstance: Any ) : ValueCreator() { val instanceParameter: KParameter = callable.instanceParameter!! + override val bucketGenerator: BucketGenerator = + BucketGenerator.forMethod(callable.parameters, companionObjectInstance) companion object { fun of(callable: KFunction): MethodValueCreator? { diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ValueCreator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ValueCreator.kt index 22513570..cd1f05ae 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ValueCreator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ValueCreator.kt @@ -21,6 +21,11 @@ internal sealed class ValueCreator { */ protected abstract val accessible: Boolean + /** + * A generator that generates an [ArgumentBucket] for binding arguments. + */ + protected abstract val bucketGenerator: BucketGenerator + /** * ValueParameters of the KFunction to be called. */ @@ -29,6 +34,11 @@ internal sealed class ValueCreator { // @see #584 val valueParameters: List get() = callable.valueParameters + /** + * @return An [ArgumentBucket] to bind arguments to. + */ + fun generateBucket(): ArgumentBucket = bucketGenerator.generate() + /** * Checking process to see if access from context is possible. * @throws IllegalAccessException @@ -45,5 +55,8 @@ internal sealed class ValueCreator { /** * Function call with default values enabled. */ - fun callBy(args: Map): T = callable.callBy(args) + fun callBy(args: ArgumentBucket): T = if (args.isFullInitialized()) + SpreadWrapper.call(callable, args.actualValues) + else + callable.callBy(args) } From a149b13ab697c7b41756a5ce94aab1e4f8a25b77 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 10 Jan 2022 14:50:35 +0900 Subject: [PATCH 05/13] fix binding process --- .../module/kotlin/KotlinValueInstantiator.kt | 37 +++---------------- 1 file changed, 5 insertions(+), 32 deletions(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt index 6fe13297..0d9906c4 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt @@ -29,25 +29,7 @@ internal class KotlinValueInstantiator( ): Any? { val valueCreator: ValueCreator<*> = cache.valueCreatorFromJava(_withArgsCreator) ?: return super.createFromObjectWith(ctxt, props, buffer) - - val propCount: Int - var numCallableParameters: Int - val callableParameters: Array - val jsonParamValueList: Array - - if (valueCreator is MethodValueCreator) { - propCount = props.size + 1 - numCallableParameters = 1 - callableParameters = arrayOfNulls(propCount) - .apply { this[0] = valueCreator.instanceParameter } - jsonParamValueList = arrayOfNulls(propCount) - .apply { this[0] = valueCreator.companionObjectInstance } - } else { - propCount = props.size - numCallableParameters = 0 - callableParameters = arrayOfNulls(propCount) - jsonParamValueList = arrayOfNulls(propCount) - } + val argumentBucket: ArgumentBucket = valueCreator.generateBucket() valueCreator.valueParameters.forEachIndexed { idx, paramDef -> val jsonProp = props[idx] @@ -115,24 +97,15 @@ internal class KotlinValueInstantiator( } } - jsonParamValueList[numCallableParameters] = paramVal - callableParameters[numCallableParameters] = paramDef - numCallableParameters++ + argumentBucket[paramDef.index] = paramVal } - return if (numCallableParameters == jsonParamValueList.size && valueCreator is ConstructorValueCreator) { + return if (valueCreator is ConstructorValueCreator && argumentBucket.isFullInitialized()) { // we didn't do anything special with default parameters, do a normal call - super.createFromObjectWith(ctxt, jsonParamValueList) + super.createFromObjectWith(ctxt, argumentBucket.actualValues) } else { valueCreator.checkAccessibility(ctxt) - - val callableParametersByName = linkedMapOf() - callableParameters.mapIndexed { idx, paramDef -> - if (paramDef != null) { - callableParametersByName[paramDef] = jsonParamValueList[idx] - } - } - valueCreator.callBy(callableParametersByName) + valueCreator.callBy(argumentBucket) } } From e08de45313b0f9fba271aaca27ce1ff04cf9edf0 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 10 Jan 2022 19:10:09 +0900 Subject: [PATCH 06/13] add generator test --- .../module/kotlin/BucketGeneratorTest.kt | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/test/kotlin/com/fasterxml/jackson/module/kotlin/BucketGeneratorTest.kt diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/BucketGeneratorTest.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/BucketGeneratorTest.kt new file mode 100644 index 00000000..f67e4c50 --- /dev/null +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/BucketGeneratorTest.kt @@ -0,0 +1,60 @@ +package com.fasterxml.jackson.module.kotlin + +import org.junit.Assert.assertArrayEquals +import org.junit.Test +import kotlin.reflect.full.functions +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class BucketGeneratorTest { + data class Data(val foo: Int, val bar: Int, val baz: Int) { + companion object { + fun creator(foo: Int, bar: Int, baz: Int) = Data(foo, bar, baz) + } + } + + @Test + fun constructorTest() { + val generator = BucketGenerator.forConstructor(::Data.parameters) + + val bucket1: ArgumentBucket = generator.generate() + + // In the case of constructor, the initial value will be empty. + assertTrue(bucket1.isEmpty()) + assertArrayEquals(Array(3) { null }, bucket1.actualValues) + + // Set one value + bucket1[0] = 0 + assertFalse(bucket1.isEmpty()) + + val bucket2: ArgumentBucket = generator.generate() + + // The initial value has not changed even after multiple regenerations. + assertTrue(bucket2.isEmpty()) + assertArrayEquals(Array(3) { null }, bucket2.actualValues) + } + + @Test + fun methodTest() { + // KFunction needs to be retrieved in a way that requires instance parameters. + val generator: BucketGenerator = Data.Companion::class.functions.first { it.name == "creator" } + .let { BucketGenerator.forMethod(it.parameters, Data.Companion) } + + val bucket1: ArgumentBucket = generator.generate() + val expectedValues = Array(4) { null }.apply { this[0] = Data.Companion } + + assertEquals(1, bucket1.size) + assertArrayEquals(expectedValues, bucket1.actualValues) + + // Set one value + bucket1[1] = 0 + assertEquals(2, bucket1.size) + + val bucket2: ArgumentBucket = generator.generate() + + // The initial value has not changed even after multiple regenerations. + assertEquals(1, bucket2.size) + assertArrayEquals(expectedValues, bucket2.actualValues) + } +} From 27dd8307cef23d2c5299a7ab9d2746a3bcb2a59b Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 10 Jan 2022 19:31:22 +0900 Subject: [PATCH 07/13] fix --- .../com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt index 13ca3a9e..3a65179f 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt @@ -111,7 +111,7 @@ internal class ArgumentBucket( override val size: Int get() = initializedCount override val values: Collection - get() = values.filterIndexed { index, _ -> isInitialized(index) } + get() = actualValues.filterIndexed { index, _ -> isInitialized(index) } override fun containsKey(key: KParameter): Boolean = isInitialized(key.index) From 2bc7bba8defab99740704c1fa0c894d61f141965 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 10 Jan 2022 19:39:26 +0900 Subject: [PATCH 08/13] add argument bucket test --- .../module/kotlin/ArgumentBucketTest.kt | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/test/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucketTest.kt diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucketTest.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucketTest.kt new file mode 100644 index 00000000..bb7c7ade --- /dev/null +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucketTest.kt @@ -0,0 +1,55 @@ +package com.fasterxml.jackson.module.kotlin + +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNull +import kotlin.test.assertTrue + +internal class ArgumentBucketTest { + data class Data(val foo: Int, val bar: Int, val baz: Int) + private val parameters = ::Data.parameters + private val generator = BucketGenerator.forConstructor(parameters) + + @Test + fun setTest() { + val bucket = generator.generate() + + assertTrue(bucket.isEmpty()) + assertNull(bucket[parameters[0]]) + + // set will succeed. + bucket[0] = 0 + assertEquals(1, bucket.size) + assertEquals(0, bucket[parameters[0]]) + + // If set the same key multiple times, the original value will not be rewritten. + bucket[0] = 1 + assertEquals(1, bucket.size) + assertEquals(0, bucket[parameters[0]]) + } + + @Test + fun isFullInitializedTest() { + val bucket = generator.generate() + + assertFalse(bucket.isFullInitialized()) + + (parameters.indices).forEach { bucket[it] = it } + + assertTrue(bucket.isFullInitialized()) + } + + @Test + fun containsValueTest() { + val bucket = generator.generate() + + assertFalse(bucket.containsValue(null)) + bucket[0] = null + assertTrue(bucket.containsValue(null)) + + assertFalse(bucket.containsValue(1)) + bucket[1] = 1 + assertTrue(bucket.containsValue(1)) + } +} From 3c7001e7f05f8a160f5e819eb6586bc016be1131 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 10 Jan 2022 20:16:27 +0900 Subject: [PATCH 09/13] add test for constructor/factory functions with the largest argument size --- .../module/kotlin/test/MaxSizeArgsTest.kt | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/MaxSizeArgsTest.kt diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/MaxSizeArgsTest.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/MaxSizeArgsTest.kt new file mode 100644 index 00000000..fb9cc24f --- /dev/null +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/MaxSizeArgsTest.kt @@ -0,0 +1,173 @@ +package com.fasterxml.jackson.module.kotlin.test + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import org.junit.Test +import kotlin.test.assertEquals + +// Test for constructor/factory functions with the largest argument size +class MaxSizeArgsTest { + data class MaxSizeConstructor( + val arg000: Int, val arg001: Int, val arg002: Int, val arg003: Int, val arg004: Int, + val arg005: Int, val arg006: Int, val arg007: Int, val arg008: Int, val arg009: Int, + val arg010: Int, val arg011: Int, val arg012: Int, val arg013: Int, val arg014: Int, + val arg015: Int, val arg016: Int, val arg017: Int, val arg018: Int, val arg019: Int, + val arg020: Int, val arg021: Int, val arg022: Int, val arg023: Int, val arg024: Int, + val arg025: Int, val arg026: Int, val arg027: Int, val arg028: Int, val arg029: Int, + val arg030: Int, val arg031: Int, val arg032: Int, val arg033: Int, val arg034: Int, + val arg035: Int, val arg036: Int, val arg037: Int, val arg038: Int, val arg039: Int, + val arg040: Int, val arg041: Int, val arg042: Int, val arg043: Int, val arg044: Int, + val arg045: Int, val arg046: Int, val arg047: Int, val arg048: Int, val arg049: Int, + val arg050: Int, val arg051: Int, val arg052: Int, val arg053: Int, val arg054: Int, + val arg055: Int, val arg056: Int, val arg057: Int, val arg058: Int, val arg059: Int, + val arg060: Int, val arg061: Int, val arg062: Int, val arg063: Int, val arg064: Int, + val arg065: Int, val arg066: Int, val arg067: Int, val arg068: Int, val arg069: Int, + val arg070: Int, val arg071: Int, val arg072: Int, val arg073: Int, val arg074: Int, + val arg075: Int, val arg076: Int, val arg077: Int, val arg078: Int, val arg079: Int, + val arg080: Int, val arg081: Int, val arg082: Int, val arg083: Int, val arg084: Int, + val arg085: Int, val arg086: Int, val arg087: Int, val arg088: Int, val arg089: Int, + val arg090: Int, val arg091: Int, val arg092: Int, val arg093: Int, val arg094: Int, + val arg095: Int, val arg096: Int, val arg097: Int, val arg098: Int, val arg099: Int, + val arg100: Int, val arg101: Int, val arg102: Int, val arg103: Int, val arg104: Int, + val arg105: Int, val arg106: Int, val arg107: Int, val arg108: Int, val arg109: Int, + val arg110: Int, val arg111: Int, val arg112: Int, val arg113: Int, val arg114: Int, + val arg115: Int, val arg116: Int, val arg117: Int, val arg118: Int, val arg119: Int, + val arg120: Int, val arg121: Int, val arg122: Int, val arg123: Int, val arg124: Int, + val arg125: Int, val arg126: Int, val arg127: Int, val arg128: Int, val arg129: Int, + val arg130: Int, val arg131: Int, val arg132: Int, val arg133: Int, val arg134: Int, + val arg135: Int, val arg136: Int, val arg137: Int, val arg138: Int, val arg139: Int, + val arg140: Int, val arg141: Int, val arg142: Int, val arg143: Int, val arg144: Int, + val arg145: Int, val arg146: Int, val arg147: Int, val arg148: Int, val arg149: Int, + val arg150: Int, val arg151: Int, val arg152: Int, val arg153: Int, val arg154: Int, + val arg155: Int, val arg156: Int, val arg157: Int, val arg158: Int, val arg159: Int, + val arg160: Int, val arg161: Int, val arg162: Int, val arg163: Int, val arg164: Int, + val arg165: Int, val arg166: Int, val arg167: Int, val arg168: Int, val arg169: Int, + val arg170: Int, val arg171: Int, val arg172: Int, val arg173: Int, val arg174: Int, + val arg175: Int, val arg176: Int, val arg177: Int, val arg178: Int, val arg179: Int, + val arg180: Int, val arg181: Int, val arg182: Int, val arg183: Int, val arg184: Int, + val arg185: Int, val arg186: Int, val arg187: Int, val arg188: Int, val arg189: Int, + val arg190: Int, val arg191: Int, val arg192: Int, val arg193: Int, val arg194: Int, + val arg195: Int, val arg196: Int, val arg197: Int, val arg198: Int, val arg199: Int, + val arg200: Int, val arg201: Int, val arg202: Int, val arg203: Int, val arg204: Int, + val arg205: Int, val arg206: Int, val arg207: Int, val arg208: Int, val arg209: Int, + val arg210: Int, val arg211: Int, val arg212: Int, val arg213: Int, val arg214: Int, + val arg215: Int, val arg216: Int, val arg217: Int, val arg218: Int, val arg219: Int, + val arg220: Int, val arg221: Int, val arg222: Int, val arg223: Int, val arg224: Int, + val arg225: Int, val arg226: Int, val arg227: Int, val arg228: Int, val arg229: Int, + val arg230: Int, val arg231: Int, val arg232: Int, val arg233: Int, val arg234: Int, + val arg235: Int, val arg236: Int, val arg237: Int, val arg238: Int, val arg239: Int, + val arg240: Int, val arg241: Int, val arg242: Int, val arg243: Int, val arg244: Int + ) { + companion object { + val defaultInstance: MaxSizeConstructor = ::MaxSizeConstructor.let { function -> + val arguments = function.parameters.map { it.index }.toTypedArray() + function.call(*arguments) + } + } + } + + @Test + fun maxConstructorTest() { + val mapper = jacksonObjectMapper() + val actual = mapper.readValue(mapper.writeValueAsString(MaxSizeConstructor.defaultInstance)) + + assertEquals(MaxSizeConstructor.defaultInstance, actual) + } + + data class MaxSizeFunction(val int: Int) { + companion object { + @JvmStatic + @JsonCreator + fun creator( + arg000: Int, arg001: Int, arg002: Int, arg003: Int, arg004: Int, + arg005: Int, arg006: Int, arg007: Int, arg008: Int, arg009: Int, + arg010: Int, arg011: Int, arg012: Int, arg013: Int, arg014: Int, + arg015: Int, arg016: Int, arg017: Int, arg018: Int, arg019: Int, + arg020: Int, arg021: Int, arg022: Int, arg023: Int, arg024: Int, + arg025: Int, arg026: Int, arg027: Int, arg028: Int, arg029: Int, + arg030: Int, arg031: Int, arg032: Int, arg033: Int, arg034: Int, + arg035: Int, arg036: Int, arg037: Int, arg038: Int, arg039: Int, + arg040: Int, arg041: Int, arg042: Int, arg043: Int, arg044: Int, + arg045: Int, arg046: Int, arg047: Int, arg048: Int, arg049: Int, + arg050: Int, arg051: Int, arg052: Int, arg053: Int, arg054: Int, + arg055: Int, arg056: Int, arg057: Int, arg058: Int, arg059: Int, + arg060: Int, arg061: Int, arg062: Int, arg063: Int, arg064: Int, + arg065: Int, arg066: Int, arg067: Int, arg068: Int, arg069: Int, + arg070: Int, arg071: Int, arg072: Int, arg073: Int, arg074: Int, + arg075: Int, arg076: Int, arg077: Int, arg078: Int, arg079: Int, + arg080: Int, arg081: Int, arg082: Int, arg083: Int, arg084: Int, + arg085: Int, arg086: Int, arg087: Int, arg088: Int, arg089: Int, + arg090: Int, arg091: Int, arg092: Int, arg093: Int, arg094: Int, + arg095: Int, arg096: Int, arg097: Int, arg098: Int, arg099: Int, + arg100: Int, arg101: Int, arg102: Int, arg103: Int, arg104: Int, + arg105: Int, arg106: Int, arg107: Int, arg108: Int, arg109: Int, + arg110: Int, arg111: Int, arg112: Int, arg113: Int, arg114: Int, + arg115: Int, arg116: Int, arg117: Int, arg118: Int, arg119: Int, + arg120: Int, arg121: Int, arg122: Int, arg123: Int, arg124: Int, + arg125: Int, arg126: Int, arg127: Int, arg128: Int, arg129: Int, + arg130: Int, arg131: Int, arg132: Int, arg133: Int, arg134: Int, + arg135: Int, arg136: Int, arg137: Int, arg138: Int, arg139: Int, + arg140: Int, arg141: Int, arg142: Int, arg143: Int, arg144: Int, + arg145: Int, arg146: Int, arg147: Int, arg148: Int, arg149: Int, + arg150: Int, arg151: Int, arg152: Int, arg153: Int, arg154: Int, + arg155: Int, arg156: Int, arg157: Int, arg158: Int, arg159: Int, + arg160: Int, arg161: Int, arg162: Int, arg163: Int, arg164: Int, + arg165: Int, arg166: Int, arg167: Int, arg168: Int, arg169: Int, + arg170: Int, arg171: Int, arg172: Int, arg173: Int, arg174: Int, + arg175: Int, arg176: Int, arg177: Int, arg178: Int, arg179: Int, + arg180: Int, arg181: Int, arg182: Int, arg183: Int, arg184: Int, + arg185: Int, arg186: Int, arg187: Int, arg188: Int, arg189: Int, + arg190: Int, arg191: Int, arg192: Int, arg193: Int, arg194: Int, + arg195: Int, arg196: Int, arg197: Int, arg198: Int, arg199: Int, + arg200: Int, arg201: Int, arg202: Int, arg203: Int, arg204: Int, + arg205: Int, arg206: Int, arg207: Int, arg208: Int, arg209: Int, + arg210: Int, arg211: Int, arg212: Int, arg213: Int, arg214: Int, + arg215: Int, arg216: Int, arg217: Int, arg218: Int, arg219: Int, + arg220: Int, arg221: Int, arg222: Int, arg223: Int, arg224: Int, + arg225: Int, arg226: Int, arg227: Int, arg228: Int, arg229: Int, + arg230: Int, arg231: Int, arg232: Int, arg233: Int, arg234: Int, + arg235: Int, arg236: Int, arg237: Int, arg238: Int, arg239: Int, + arg240: Int, arg241: Int, arg242: Int, arg243: Int, arg244: Int, + arg245: Int, arg246: Int, arg247: Int, arg248: Int, arg249: Int, + arg250: Int, arg251: Int, arg252: Int, arg253: Int + ) = MaxSizeFunction( + arg000 + arg001 + arg002 + arg003 + arg004 + arg005 + arg006 + arg007 + arg008 + arg009 + + arg010 + arg011 + arg012 + arg013 + arg014 + arg015 + arg016 + arg017 + arg018 + arg019 + + arg020 + arg021 + arg022 + arg023 + arg024 + arg025 + arg026 + arg027 + arg028 + arg029 + + arg030 + arg031 + arg032 + arg033 + arg034 + arg035 + arg036 + arg037 + arg038 + arg039 + + arg040 + arg041 + arg042 + arg043 + arg044 + arg045 + arg046 + arg047 + arg048 + arg049 + + arg050 + arg051 + arg052 + arg053 + arg054 + arg055 + arg056 + arg057 + arg058 + arg059 + + arg060 + arg061 + arg062 + arg063 + arg064 + arg065 + arg066 + arg067 + arg068 + arg069 + + arg070 + arg071 + arg072 + arg073 + arg074 + arg075 + arg076 + arg077 + arg078 + arg079 + + arg080 + arg081 + arg082 + arg083 + arg084 + arg085 + arg086 + arg087 + arg088 + arg089 + + arg090 + arg091 + arg092 + arg093 + arg094 + arg095 + arg096 + arg097 + arg098 + arg099 + + arg100 + arg101 + arg102 + arg103 + arg104 + arg105 + arg106 + arg107 + arg108 + arg109 + + arg110 + arg111 + arg112 + arg113 + arg114 + arg115 + arg116 + arg117 + arg118 + arg119 + + arg120 + arg121 + arg122 + arg123 + arg124 + arg125 + arg126 + arg127 + arg128 + arg129 + + arg130 + arg131 + arg132 + arg133 + arg134 + arg135 + arg136 + arg137 + arg138 + arg139 + + arg140 + arg141 + arg142 + arg143 + arg144 + arg145 + arg146 + arg147 + arg148 + arg149 + + arg150 + arg151 + arg152 + arg153 + arg154 + arg155 + arg156 + arg157 + arg158 + arg159 + + arg160 + arg161 + arg162 + arg163 + arg164 + arg165 + arg166 + arg167 + arg168 + arg169 + + arg170 + arg171 + arg172 + arg173 + arg174 + arg175 + arg176 + arg177 + arg178 + arg179 + + arg180 + arg181 + arg182 + arg183 + arg184 + arg185 + arg186 + arg187 + arg188 + arg189 + + arg190 + arg191 + arg192 + arg193 + arg194 + arg195 + arg196 + arg197 + arg198 + arg199 + + arg200 + arg201 + arg202 + arg203 + arg204 + arg205 + arg206 + arg207 + arg208 + arg209 + + arg210 + arg211 + arg212 + arg213 + arg214 + arg215 + arg216 + arg217 + arg218 + arg219 + + arg220 + arg221 + arg222 + arg223 + arg224 + arg225 + arg226 + arg227 + arg228 + arg229 + + arg230 + arg231 + arg232 + arg233 + arg234 + arg235 + arg236 + arg237 + arg238 + arg239 + + arg240 + arg241 + arg242 + arg243 + arg244 + arg245 + arg246 + arg247 + arg248 + arg249 + + arg250 + arg251 + arg252 + arg253 + ) + } + } + + @Test + fun maxSizeFunctionTest() { + val mapper = jacksonObjectMapper() + val src = (0..253).associateBy { "arg${"%03d".format(it)}" }.let { mapper.writeValueAsString(it) } + + val actual = mapper.readValue(src) + assertEquals(MaxSizeFunction((0..253).sum()), actual) + } +} From ad5f05bce6aa0a62e4f77e3e90823bfe37d1e749 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 10 Jan 2022 21:55:22 +0900 Subject: [PATCH 10/13] Remove variables that are no longer in use by external parties --- .../com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt index e3891ca9..78346247 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt @@ -1,7 +1,6 @@ package com.fasterxml.jackson.module.kotlin import kotlin.reflect.KFunction -import kotlin.reflect.KParameter import kotlin.reflect.full.extensionReceiverParameter import kotlin.reflect.full.instanceParameter import kotlin.reflect.jvm.isAccessible @@ -9,9 +8,8 @@ import kotlin.reflect.jvm.isAccessible internal class MethodValueCreator private constructor( override val callable: KFunction, override val accessible: Boolean, - val companionObjectInstance: Any + companionObjectInstance: Any ) : ValueCreator() { - val instanceParameter: KParameter = callable.instanceParameter!! override val bucketGenerator: BucketGenerator = BucketGenerator.forMethod(callable.parameters, companionObjectInstance) From c020a871a455a3fd57e172cc3fdeb41664ab820b Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 10 Jan 2022 22:58:10 +0900 Subject: [PATCH 11/13] add call test for MethodValueCreator --- pom.xml | 7 ++++ .../module/kotlin/MethodValueCreatorTest.kt | 40 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/test/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreatorTest.kt diff --git a/pom.xml b/pom.xml index ce5154ca..f6daa57a 100644 --- a/pom.xml +++ b/pom.xml @@ -121,6 +121,13 @@ jackson-dataformat-xml test + + + io.mockk + mockk + 1.12.2 + test + diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreatorTest.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreatorTest.kt new file mode 100644 index 00000000..38cd7aa2 --- /dev/null +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreatorTest.kt @@ -0,0 +1,40 @@ +package com.fasterxml.jackson.module.kotlin + +import io.mockk.spyk +import io.mockk.verify +import org.junit.Test +import kotlin.reflect.KFunction +import kotlin.reflect.full.functions +import kotlin.test.assertEquals + +class MethodValueCreatorTest { + data class Data(val value: Int) { + companion object { + fun target(value: Int = -1) = Data(value) + } + } + + companion object { + private val targetFunction: KFunction<*> = spyk(Data.Companion::class.functions.first { it.name == "target" }) + private val methodValueCreator = MethodValueCreator.of(targetFunction)!! + } + + @Test + fun withDefaultValue() { + val actual = methodValueCreator.generateBucket().let { methodValueCreator.callBy(it) } + assertEquals(Data(-1), actual) + verify(exactly = 1) { targetFunction.callBy(any()) } + } + + @Test + fun withoutDefaultValue() { + val actual = methodValueCreator.generateBucket().let { + it[1] = 1 + methodValueCreator.callBy(it) + } + assertEquals(Data(1), actual) + // If the argument is fully initialized, call is used instead of callBy + verify(exactly = 1) { targetFunction.call(*anyVararg()) } + verify(exactly = 0) { targetFunction.callBy(any()) } + } +} From 0d7d3e47195a4f49035485a3614136e978049487 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Tue, 18 Jan 2022 22:27:40 +0900 Subject: [PATCH 12/13] fix to use arrayOfNulls --- .../com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt index 3a65179f..530bf0dc 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt @@ -46,7 +46,7 @@ internal class BucketGenerator private constructor( fun forConstructor(parameters: List): BucketGenerator { val paramSize = parameters.size // Since the constructor does not require any instance parameters, do not operation the values. - return BucketGenerator(paramSize, Array(paramSize) { null }, getOriginalMasks(paramSize), 0, parameters) + return BucketGenerator(paramSize, arrayOfNulls(paramSize), getOriginalMasks(paramSize), 0, parameters) } /** @@ -59,7 +59,7 @@ internal class BucketGenerator private constructor( // In the jackson-module-kotlin process, instance parameters are always at the top, // so they should be placed at the top of originalValues. - val originalValues = Array(paramSize) { null }.apply { this[0] = instance } + val originalValues = arrayOfNulls(paramSize).apply { this[0] = instance } // Since the instance parameters have already been initialized, // the originalMasks must also be in the corresponding state. val originalMasks = getOriginalMasks(paramSize).apply { this[0] = this[0] and 1.inv() } From cf64d24bd315818144def09c9b4dc5dc34f56980 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Mar 2023 11:14:54 +0900 Subject: [PATCH 13/13] Update dependency --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index f6daa57a..335689ad 100644 --- a/pom.xml +++ b/pom.xml @@ -121,11 +121,11 @@ jackson-dataformat-xml test - + io.mockk - mockk - 1.12.2 + mockk-jvm + 1.13.4 test