From 11787a40e0b2b0e7c55fd863d5aa1fdcdc4c8d96 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 10 Dec 2023 17:00:00 +0900 Subject: [PATCH 1/3] Fix generics UNCHECKED_CAST was required throughout and was not contributing to improved safety. --- .../jackson/module/kotlin/KotlinAnnotationIntrospector.kt | 3 +-- .../module/kotlin/KotlinNamesAnnotationIntrospector.kt | 3 +-- .../com/fasterxml/jackson/module/kotlin/ReflectionCache.kt | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt index bd61b979..7af632b4 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt @@ -95,9 +95,8 @@ internal class KotlinAnnotationIntrospector( if (!useJavaDurationConversion) return null return (a as? AnnotatedParameter)?.let { param -> - @Suppress("UNCHECKED_CAST") val function: KFunction<*> = when (val owner = param.owner.member) { - is Constructor<*> -> cache.kotlinFromJava(owner as Constructor) + is Constructor<*> -> cache.kotlinFromJava(owner) is Method -> cache.kotlinFromJava(owner) else -> null } ?: return@let null diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt index babe8e92..d4af91b9 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt @@ -79,13 +79,12 @@ internal class KotlinNamesAnnotationIntrospector( } } - @Suppress("UNCHECKED_CAST") private fun hasCreatorAnnotation(member: AnnotatedConstructor): Boolean { // don't add a JsonCreator to any constructor if one is declared already val kClass = member.declaringClass.kotlin .apply { if (this in ignoredClassesForImplyingJsonCreator) return false } - val kConstructor = cache.kotlinFromJava(member.annotated as Constructor) ?: return false + val kConstructor = cache.kotlinFromJava(member.annotated) ?: return false // TODO: should we do this check or not? It could cause failures if we miss another way a property could be set // val requiredProperties = kClass.declaredMemberProperties.filter {!it.returnType.isMarkedNullable }.map { it.name }.toSet() diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt index dbcb6b17..b758640e 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt @@ -42,7 +42,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { } } - private val javaConstructorToKotlin = LRUMap, KFunction>(reflectionCacheSize, reflectionCacheSize) + private val javaConstructorToKotlin = LRUMap, KFunction<*>>(reflectionCacheSize, reflectionCacheSize) private val javaMethodToKotlin = LRUMap>(reflectionCacheSize, reflectionCacheSize) private val javaExecutableToValueCreator = LRUMap>(reflectionCacheSize, reflectionCacheSize) private val javaConstructorIsCreatorAnnotated = LRUMap(reflectionCacheSize, reflectionCacheSize) @@ -57,7 +57,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { private val valueClassBoxConverterCache: LRUMap, ValueClassBoxConverter<*, *>> = LRUMap(0, reflectionCacheSize) - fun kotlinFromJava(key: Constructor): KFunction? = javaConstructorToKotlin.get(key) + fun kotlinFromJava(key: Constructor<*>): KFunction<*>? = javaConstructorToKotlin.get(key) ?: key.kotlinFunction?.let { javaConstructorToKotlin.putIfAbsent(key, it) ?: it } fun kotlinFromJava(key: Method): KFunction<*>? = javaMethodToKotlin.get(key) From 3c3d350d495bd7183fda69bcd71a75322f7347e4 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 10 Dec 2023 17:05:32 +0900 Subject: [PATCH 2/3] Reduce conversion cache from Executable to KFunction --- .../jackson/module/kotlin/ReflectionCache.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt index b758640e..eef3e61b 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt @@ -19,7 +19,7 @@ import kotlin.reflect.jvm.kotlinFunction internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { companion object { // Increment is required when properties that use LRUMap are changed. - private const val serialVersionUID = 1L + private const val serialVersionUID = 2L } sealed class BooleanTriState(val value: Boolean?) { @@ -42,8 +42,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { } } - private val javaConstructorToKotlin = LRUMap, KFunction<*>>(reflectionCacheSize, reflectionCacheSize) - private val javaMethodToKotlin = LRUMap>(reflectionCacheSize, reflectionCacheSize) + private val javaExecutableToKotlin = LRUMap>(reflectionCacheSize, reflectionCacheSize) private val javaExecutableToValueCreator = LRUMap>(reflectionCacheSize, reflectionCacheSize) private val javaConstructorIsCreatorAnnotated = LRUMap(reflectionCacheSize, reflectionCacheSize) private val javaMemberIsRequired = LRUMap(reflectionCacheSize, reflectionCacheSize) @@ -57,11 +56,11 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { private val valueClassBoxConverterCache: LRUMap, ValueClassBoxConverter<*, *>> = LRUMap(0, reflectionCacheSize) - fun kotlinFromJava(key: Constructor<*>): KFunction<*>? = javaConstructorToKotlin.get(key) - ?: key.kotlinFunction?.let { javaConstructorToKotlin.putIfAbsent(key, it) ?: it } + fun kotlinFromJava(key: Constructor<*>): KFunction<*>? = javaExecutableToKotlin.get(key) + ?: key.kotlinFunction?.let { javaExecutableToKotlin.putIfAbsent(key, it) ?: it } - fun kotlinFromJava(key: Method): KFunction<*>? = javaMethodToKotlin.get(key) - ?: key.kotlinFunction?.let { javaMethodToKotlin.putIfAbsent(key, it) ?: it } + fun kotlinFromJava(key: Method): KFunction<*>? = javaExecutableToKotlin.get(key) + ?: key.kotlinFunction?.let { javaExecutableToKotlin.putIfAbsent(key, it) ?: it } /** * return null if... From a8b3adc798b6a69a884932b0c54a12b0a8b287b0 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 10 Dec 2023 17:20:16 +0900 Subject: [PATCH 3/3] Update release notes wrt #740 --- release-notes/CREDITS-2.x | 1 + release-notes/VERSION-2.x | 2 ++ 2 files changed, 3 insertions(+) diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index ca9d0d67..30a045ad 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -18,6 +18,7 @@ Contributors: # 2.17.0 (not yet released) WrongWrong (@k163377) +* #740: Reduce conversion cache from Executable to KFunction. * #738: Fix JacksonInject priority. * #732: SequenceSerializer removed. * #727: Fixed overriding findCreatorAnnotation instead of hasCreatorAnnotation diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 628d8be7..245c49ea 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -18,6 +18,8 @@ Co-maintainers: 2.17.0 (not yet released) +#740: Reduce conversion cache from Executable to KFunction. + This will reduce memory usage efficiency and total memory consumption, but may result in a minor performance degradation in use cases where a large number of factory functions are used as JsonCreator. #738: JacksonInject is now preferred over the default argument(fixes #722). #732: SequenceSerializer removed. #727: Fixed overriding findCreatorAnnotation instead of hasCreatorAnnotation.