Skip to content

Commit ef318c2

Browse files
committed
Add ValueClassBoxConverter cache
Add ValueClassBoxConverter cache This cache has been confirmed on kogera to have the effect of speeding up initialization. from https://github.com/ProjectMapK/jackson-module-kogera/blob/da6d05cb36e5584b991e092a4eabd050909a1677/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt
1 parent 71d1f36 commit ef318c2

File tree

1 file changed

+53
-0
lines changed

1 file changed

+53
-0
lines changed

src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import java.io.Serializable
99
import java.lang.reflect.Constructor
1010
import java.lang.reflect.Executable
1111
import java.lang.reflect.Method
12+
import java.util.*
13+
import kotlin.reflect.KClass
1214
import kotlin.reflect.KFunction
15+
import kotlin.reflect.full.memberProperties
16+
import kotlin.reflect.jvm.javaGetter
1317
import kotlin.reflect.jvm.kotlinFunction
1418

1519
internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
@@ -44,6 +48,15 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
4448
private val javaConstructorIsCreatorAnnotated = LRUMap<AnnotatedConstructor, Boolean>(reflectionCacheSize, reflectionCacheSize)
4549
private val javaMemberIsRequired = LRUMap<AnnotatedMember, BooleanTriState?>(reflectionCacheSize, reflectionCacheSize)
4650

51+
// Initial size is 0 because the value class is not always used
52+
private val valueClassReturnTypeCache: LRUMap<AnnotatedMethod, Optional<KClass<*>>> =
53+
LRUMap(0, reflectionCacheSize)
54+
55+
// TODO: Consider whether the cache size should be reduced more,
56+
// since the cache is used only twice locally at initialization per property.
57+
private val valueClassBoxConverterCache: LRUMap<KClass<*>, ValueClassBoxConverter<*, *>> =
58+
LRUMap(0, reflectionCacheSize)
59+
4760
fun kotlinFromJava(key: Constructor<Any>): KFunction<Any>? = javaConstructorToKotlin.get(key)
4861
?: key.kotlinFunction?.let { javaConstructorToKotlin.putIfAbsent(key, it) ?: it }
4962

@@ -87,4 +100,44 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
87100

88101
fun javaMemberIsRequired(key: AnnotatedMember, calc: (AnnotatedMember) -> Boolean?): Boolean? = javaMemberIsRequired.get(key)?.value
89102
?: calc(key).let { javaMemberIsRequired.putIfAbsent(key, BooleanTriState.fromBoolean(it))?.value ?: it }
103+
104+
private fun AnnotatedMethod.getValueClassReturnType(): KClass<*>? {
105+
val getter = this.member.apply {
106+
// If the return value of the getter is a value class,
107+
// it will be serialized properly without doing anything.
108+
// TODO: Verify the case where a value class encompasses another value class.
109+
if (this.returnType.isUnboxableValueClass()) return null
110+
}
111+
112+
// Extract the return type from the property or getter-like.
113+
val kotlinReturnType = getter.declaringClass.kotlin
114+
.let { kClass ->
115+
// KotlinReflectionInternalError is raised in GitHub167 test,
116+
// but it looks like an edge case, so it is ignored.
117+
val prop = runCatching { kClass.memberProperties }.getOrNull()?.find { it.javaGetter == getter }
118+
(prop?.returnType ?: runCatching { getter.kotlinFunction }.getOrNull()?.returnType)
119+
?.classifier as? KClass<*>
120+
} ?: return null
121+
122+
// Since there was no way to directly determine whether returnType is a value class or not,
123+
// Class is restored and processed.
124+
return kotlinReturnType.takeIf { it.isValue }
125+
}
126+
127+
fun findValueClassReturnType(getter: AnnotatedMethod): KClass<*>? {
128+
val optional = valueClassReturnTypeCache.get(getter)
129+
130+
return if (optional != null) {
131+
optional
132+
} else {
133+
val value = Optional.ofNullable(getter.getValueClassReturnType())
134+
(valueClassReturnTypeCache.putIfAbsent(getter, value) ?: value)
135+
}.orElse(null)
136+
}
137+
138+
fun getValueClassBoxConverter(unboxedClass: Class<*>, valueClass: KClass<*>): ValueClassBoxConverter<*, *> =
139+
valueClassBoxConverterCache.get(valueClass) ?: run {
140+
val value = ValueClassBoxConverter(unboxedClass, valueClass)
141+
(valueClassBoxConverterCache.putIfAbsent(valueClass, value) ?: value)
142+
}
90143
}

0 commit comments

Comments
 (0)