Skip to content

Commit

Permalink
Merge pull request #155 from ProjectMapK/fix/cache
Browse files Browse the repository at this point in the history
Cache efficiency for boxed return types
  • Loading branch information
k163377 authored Oct 8, 2023
2 parents 248d372 + 9df7daa commit fe712e9
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.projectmapk.jackson.module.kogera
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod
import com.fasterxml.jackson.databind.util.LRUMap
import java.io.Serializable
import java.lang.reflect.Method
import java.util.Optional

internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
Expand All @@ -16,7 +17,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
private val classCache = LRUMap<Class<*>, JmClass>(reflectionCacheSize, reflectionCacheSize)

// Initial size is 0 because the value class is not always used
private val valueClassReturnTypeCache: LRUMap<AnnotatedMethod, Optional<Class<*>>> =
private val valueClassReturnTypeCache: LRUMap<Method, Optional<Class<*>>> =
LRUMap(0, reflectionCacheSize)

// TODO: Consider whether the cache size should be reduced more,
Expand Down Expand Up @@ -46,28 +47,29 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
}
}

private fun AnnotatedMethod.getValueClassReturnType(): Class<*>? {
val getter = this.member.apply {
// If the return value of the getter is a value class,
// it will be serialized properly without doing anything.
// TODO: Verify the case where a value class encompasses another value class.
if (this.returnType.isUnboxableValueClass()) return null
}
val kotlinProperty = getJmClass(getter.declaringClass)?.findPropertyByGetter(getter)
private fun Method.getValueClassReturnType(): Class<*>? {
val kotlinProperty = getJmClass(declaringClass)?.findPropertyByGetter(this)

// Since there was no way to directly determine whether returnType is a value class or not,
// Class is restored and processed.
return kotlinProperty?.returnType?.reconstructClassOrNull()?.takeIf { it.isUnboxableValueClass() }
}

fun findValueClassReturnType(getter: AnnotatedMethod): Class<*>? {
val optional = valueClassReturnTypeCache.get(getter)
// Return boxed type on Kotlin for unboxed getters
fun findBoxedReturnType(getter: AnnotatedMethod): Class<*>? {
val method = getter.member
val optional = valueClassReturnTypeCache.get(method)

return if (optional != null) {
optional
} else {
val value = Optional.ofNullable(getter.getValueClassReturnType())
(valueClassReturnTypeCache.putIfAbsent(getter, value) ?: value)
// If the return value of the getter is a value class,
// it will be serialized properly without doing anything.
// TODO: Verify the case where a value class encompasses another value class.
if (method.returnType.isUnboxableValueClass()) return null

val value = Optional.ofNullable(method.getValueClassReturnType())
(valueClassReturnTypeCache.putIfAbsent(method, value) ?: value)
}.orElse(null)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ internal class KotlinFallbackAnnotationIntrospector(

override fun findSerializationConverter(a: Annotated): Converter<*, *>? = when (a) {
// Find a converter to handle the case where the getter returns an unboxed value from the value class.
is AnnotatedMethod -> cache.findValueClassReturnType(a)?.let {
is AnnotatedMethod -> cache.findBoxedReturnType(a)?.let {
if (useJavaDurationConversion && it == KotlinDuration::class.java) {
if (a.rawReturnType == KotlinDuration::class.java) {
KotlinToJavaDurationConverter
Expand Down Expand Up @@ -110,7 +110,7 @@ internal class KotlinFallbackAnnotationIntrospector(
// Perform proper serialization even if the value wrapped by the value class is null.
// If value is a non-null object type, it must not be reboxing.
override fun findNullSerializer(am: Annotated): JsonSerializer<*>? = (am as? AnnotatedMethod)?.let { _ ->
cache.findValueClassReturnType(am)?.let {
cache.findBoxedReturnType(am)?.let {
if (it.requireRebox()) cache.getValueClassBoxConverter(am.rawReturnType, it).delegatingSerializer else null
}
}
Expand Down

0 comments on commit fe712e9

Please sign in to comment.