Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce conversion cache from Executable to KFunction #740

Merged
merged 3 commits into from
Dec 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.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
Expand Down
2 changes: 2 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Any>)
is Constructor<*> -> cache.kotlinFromJava(owner)
is Method -> cache.kotlinFromJava(owner)
else -> null
} ?: return@let null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Any>) ?: 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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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?) {
Expand All @@ -42,8 +42,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
}
}

private val javaConstructorToKotlin = LRUMap<Constructor<Any>, KFunction<Any>>(reflectionCacheSize, reflectionCacheSize)
private val javaMethodToKotlin = LRUMap<Method, KFunction<*>>(reflectionCacheSize, reflectionCacheSize)
private val javaExecutableToKotlin = LRUMap<Executable, KFunction<*>>(reflectionCacheSize, reflectionCacheSize)
private val javaExecutableToValueCreator = LRUMap<Executable, ValueCreator<*>>(reflectionCacheSize, reflectionCacheSize)
private val javaConstructorIsCreatorAnnotated = LRUMap<AnnotatedConstructor, Boolean>(reflectionCacheSize, reflectionCacheSize)
private val javaMemberIsRequired = LRUMap<AnnotatedMember, BooleanTriState?>(reflectionCacheSize, reflectionCacheSize)
Expand All @@ -57,11 +56,11 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
private val valueClassBoxConverterCache: LRUMap<KClass<*>, ValueClassBoxConverter<*, *>> =
LRUMap(0, reflectionCacheSize)

fun kotlinFromJava(key: Constructor<Any>): KFunction<Any>? = 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...
Expand Down