diff --git a/pom.xml b/pom.xml index ca1e624c..5c432058 100644 --- a/pom.xml +++ b/pom.xml @@ -114,7 +114,6 @@ - ${project.basedir}/src/main/kotlin ${project.basedir}/src/test/kotlin @@ -167,6 +166,10 @@ true true + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/main/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..1f39fa53 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/kotlin/SpreadWrapper.java @@ -0,0 +1,10 @@ +package com.fasterxml.jackson.module.kotlin; + +import kotlin.reflect.KFunction; + +class SpreadWrapper { + // Wrapper to avoid costly calls using spread operator. + static T call(KFunction function, Object[] args) { + return function.call(args); + } +} 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..b27422c7 --- /dev/null +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ArgumentBucket.kt @@ -0,0 +1,47 @@ +package com.fasterxml.jackson.module.kotlin + +import java.util.AbstractMap.SimpleEntry as Entry +import kotlin.reflect.KParameter + +internal class ArgumentBucket(private val parameters: List) : Map { + // This array is sorted by KParameter::index. + val valueArray: Array = arrayOfNulls(parameters.size) + private val initializationStatuses: BooleanArray = BooleanArray(parameters.size) + private var count = 0 + + operator fun set(key: KParameter, value: Any?): Any? { + return valueArray[key.index].apply { + valueArray[key.index] = value + + if (!initializationStatuses[key.index]) { + initializationStatuses[key.index] = true + count++ + } + } + } + + fun isFullInitialized(): Boolean = parameters.size == count + + override val entries: Set> + get() = valueArray.withIndex() + .filter { (i, _) -> initializationStatuses[i] } + .map { (i, arg) -> Entry(parameters[i], arg) } + .toSet() + override val keys: Set + get() = parameters + .filterIndexed { i, _ -> initializationStatuses[i] } + .toSet() + override val size: Int + get() = count + override val values: Collection + get() = valueArray.filterIndexed { i, _ -> initializationStatuses[i] } + + override fun containsKey(key: KParameter): Boolean = initializationStatuses[key.index] + + override fun containsValue(value: Any?): Boolean = valueArray.withIndex() + .any { (i, arg) -> initializationStatuses[i] && value == arg } + + override fun get(key: KParameter): Any? = valueArray[key.index] + + override fun isEmpty(): Boolean = count == 0 +} 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 ee55e949..27e80018 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt @@ -162,13 +162,18 @@ internal class KotlinValueInstantiator( ) { callable.isAccessible = true } - val callableParametersByName = linkedMapOf() - callableParameters.mapIndexed { idx, paramDef -> - if (paramDef != null) { - callableParametersByName[paramDef] = jsonParamValueList[idx] + ArgumentBucket(callable.parameters).apply { + callableParameters.forEachIndexed { idx, paramDef -> + if (paramDef != null) { + this[paramDef] = jsonParamValueList[idx] + } } + }.let { + if (it.isFullInitialized()) + SpreadWrapper.call(callable, it.valueArray) + else + callable.callBy(it) } - callable.callBy(callableParametersByName) } }