Skip to content

Commit

Permalink
Merge pull request #687 from k163377/feat/refactor-valueinstantiator
Browse files Browse the repository at this point in the history
Optimize and Refactor KotlinValueInstantiator.createFromObjectWith
  • Loading branch information
k163377 committed Jul 15, 2023
2 parents ef6737d + 578ce84 commit 4bd4845
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 30 deletions.
1 change: 1 addition & 0 deletions release-notes/CREDITS-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Contributors:
# 2.16.0 (not yet released)

WrongWrong (@k163377)
* #687: Optimize and Refactor KotlinValueInstantiator.createFromObjectWith
* #685: Streamline default value management for KotlinFeatures
* #684: Update Kotlin Version to 1.6
* #682: Remove MissingKotlinParameterException and replace with MismatchedInputException
Expand Down
1 change: 1 addition & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Co-maintainers:

2.16.0 (not yet released)

#687: Optimize and Refactor KotlinValueInstantiator.createFromObjectWith.
#685: Streamline default value management for KotlinFeatures.
This improves the initialization cost of kotlin-module a little.
#684: Kotlin 1.5 has been deprecated and the minimum supported Kotlin version will be updated to 1.6.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.fasterxml.jackson.module.kotlin
import com.fasterxml.jackson.databind.BeanDescription
import com.fasterxml.jackson.databind.DeserializationConfig
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.deser.SettableBeanProperty
import com.fasterxml.jackson.databind.deser.ValueInstantiator
import com.fasterxml.jackson.databind.deser.ValueInstantiators
Expand All @@ -13,6 +14,7 @@ import com.fasterxml.jackson.databind.exc.MismatchedInputException
import java.lang.reflect.TypeVariable
import kotlin.reflect.KParameter
import kotlin.reflect.KType
import kotlin.reflect.KTypeProjection
import kotlin.reflect.jvm.javaType

internal class KotlinValueInstantiator(
Expand All @@ -23,6 +25,13 @@ internal class KotlinValueInstantiator(
private val nullIsSameAsDefault: Boolean,
private val strictNullChecks: Boolean
) : StdValueInstantiator(src) {
private fun JavaType.requireEmptyValue() =
(nullToEmptyCollection && this.isCollectionLikeType) || (nullToEmptyMap && this.isMapLikeType)

private fun KType.isGenericTypeVar() = javaType is TypeVariable<*>

private fun List<KTypeProjection>.markedNonNullAt(index: Int) = getOrNull(index)?.type?.isMarkedNullable == false

override fun createFromObjectWith(
ctxt: DeserializationContext,
props: Array<out SettableBeanProperty>,
Expand Down Expand Up @@ -58,14 +67,15 @@ internal class KotlinValueInstantiator(
return@forEachIndexed
}

val paramType = paramDef.type
var paramVal = if (!isMissing || paramDef.isPrimitive() || jsonProp.hasInjectableValueId()) {
val tempParamVal = buffer.getParameter(jsonProp)
if (nullIsSameAsDefault && tempParamVal == null && paramDef.isOptional) {
return@forEachIndexed
}
tempParamVal
} else {
if(paramDef.type.isMarkedNullable) {
if(paramType.isMarkedNullable) {
// do not try to create any object if it is nullable and the value is missing
null
} else {
Expand All @@ -74,46 +84,51 @@ internal class KotlinValueInstantiator(
}
}

if (paramVal == null && ((nullToEmptyCollection && jsonProp.type.isCollectionLikeType) || (nullToEmptyMap && jsonProp.type.isMapLikeType))) {
paramVal = NullsAsEmptyProvider(jsonProp.valueDeserializer).getNullValue(ctxt)
}
val propType = jsonProp.type

val isGenericTypeVar = paramDef.type.javaType is TypeVariable<*>
val isMissingAndRequired = paramVal == null && isMissing && jsonProp.isRequired
if (isMissingAndRequired ||
(!isGenericTypeVar && paramVal == null && !paramDef.type.isMarkedNullable)) {
throw MismatchedInputException.from(
ctxt.parser,
jsonProp.type,
"Instantiation of $valueTypeDesc value failed for JSON property ${jsonProp.name} " +
"due to missing (therefore NULL) value for creator parameter ${paramDef.name} " +
"which is a non-nullable type"
).wrapWithPath(this.valueClass, jsonProp.name)
}
if (paramVal == null) {
if (propType.requireEmptyValue()) {
paramVal = NullsAsEmptyProvider(jsonProp.valueDeserializer).getNullValue(ctxt)
} else {
val isMissingAndRequired = isMissing && jsonProp.isRequired

// Since #310 reported that the calculation cost is high, isGenericTypeVar is determined last.
if (isMissingAndRequired || (!paramType.isMarkedNullable && !paramType.isGenericTypeVar())) {
throw MismatchedInputException.from(
ctxt.parser,
propType,
"Instantiation of $valueTypeDesc value failed for JSON property ${jsonProp.name} " +
"due to missing (therefore NULL) value for creator parameter ${paramDef.name} " +
"which is a non-nullable type"
).wrapWithPath(this.valueClass, jsonProp.name)
}
}
} else if (strictNullChecks) {
val arguments = paramType.arguments

if (strictNullChecks && paramVal != null) {
var paramType: String? = null
var paramTypeStr: String? = null
var itemType: KType? = null
if (jsonProp.type.isCollectionLikeType && paramDef.type.arguments.getOrNull(0)?.type?.isMarkedNullable == false && (paramVal as Collection<*>).any { it == null }) {
paramType = "collection"
itemType = paramDef.type.arguments[0].type

if (propType.isCollectionLikeType && arguments.markedNonNullAt(0) && (paramVal as Collection<*>).any { it == null }) {
paramTypeStr = "collection"
itemType = arguments[0].type
}

if (jsonProp.type.isMapLikeType && paramDef.type.arguments.getOrNull(1)?.type?.isMarkedNullable == false && (paramVal as Map<*, *>).any { it.value == null }) {
paramType = "map"
itemType = paramDef.type.arguments[1].type
if (propType.isMapLikeType && arguments.markedNonNullAt(1) && (paramVal as Map<*, *>).any { it.value == null }) {
paramTypeStr = "map"
itemType = arguments[1].type
}

if (jsonProp.type.isArrayType && paramDef.type.arguments.getOrNull(0)?.type?.isMarkedNullable == false && (paramVal as Array<*>).any { it == null }) {
paramType = "array"
itemType = paramDef.type.arguments[0].type
if (propType.isArrayType && arguments.markedNonNullAt(0) && (paramVal as Array<*>).any { it == null }) {
paramTypeStr = "array"
itemType = arguments[0].type
}

if (paramType != null && itemType != null) {
if (paramTypeStr != null && itemType != null) {
throw MismatchedInputException.from(
ctxt.parser,
jsonProp.type,
"Instantiation of $itemType $paramType failed for JSON property ${jsonProp.name} due to null value in a $paramType that does not allow null values"
propType,
"Instantiation of $itemType $paramTypeStr failed for JSON property ${jsonProp.name} due to null value in a $paramTypeStr that does not allow null values"
).wrapWithPath(this.valueClass, jsonProp.name)
}
}
Expand Down

0 comments on commit 4bd4845

Please sign in to comment.