Skip to content

Commit

Permalink
Fixes handling of ReflectionCache.
Browse files Browse the repository at this point in the history
  • Loading branch information
k163377 committed May 14, 2021
1 parent 4bd73bb commit c320833
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 78 deletions.
Expand Up @@ -77,11 +77,9 @@ class KotlinModule constructor (
}

val cache = ReflectionCache(reflectionCacheSize)
val cacheNew = ReflectionCacheNew(reflectionCacheSize)

context.addValueInstantiators(KotlinInstantiators(
cache,
cacheNew,
nullToEmptyCollection,
nullToEmptyMap,
nullIsSameAsDefault,
Expand Down
Expand Up @@ -26,7 +26,6 @@ import kotlin.reflect.jvm.javaType
internal class KotlinValueInstantiator(
src: StdValueInstantiator,
private val cache: ReflectionCache,
private val cacheNew: ReflectionCacheNew,
private val nullToEmptyCollection: Boolean,
private val nullToEmptyMap: Boolean,
private val nullIsSameAsDefault: Boolean,
Expand All @@ -38,7 +37,7 @@ internal class KotlinValueInstantiator(
props: Array<out SettableBeanProperty>,
buffer: PropertyValueBuffer
): Any? {
val instantiator: Instantiator<*> = cacheNew.instantiatorFromJava(_withArgsCreator)
val instantiator: Instantiator<*> = cache.instantiatorFromJava(_withArgsCreator)
?: return super.createFromObjectWith(ctxt, props, buffer) // we cannot reflect this method so do the default Java-ish behavior

val bucket = instantiator.generateBucket()
Expand Down Expand Up @@ -280,7 +279,6 @@ internal class KotlinValueInstantiator(

internal class KotlinInstantiators(
private val cache: ReflectionCache,
private val cacheNew: ReflectionCacheNew,
private val nullToEmptyCollection: Boolean,
private val nullToEmptyMap: Boolean,
private val nullIsSameAsDefault: Boolean,
Expand All @@ -297,7 +295,6 @@ internal class KotlinInstantiators(
KotlinValueInstantiator(
defaultInstantiator,
cache,
cacheNew,
nullToEmptyCollection,
nullToEmptyMap,
nullIsSameAsDefault,
Expand Down
Expand Up @@ -3,11 +3,14 @@ package com.fasterxml.jackson.module.kotlin
import com.fasterxml.jackson.databind.introspect.AnnotatedConstructor
import com.fasterxml.jackson.databind.introspect.AnnotatedMember
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod
import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams
import com.fasterxml.jackson.databind.util.LRUMap
import java.lang.reflect.Constructor
import java.lang.reflect.Method
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.full.extensionReceiverParameter
import kotlin.reflect.full.instanceParameter
import kotlin.reflect.jvm.kotlinFunction


Expand Down Expand Up @@ -38,7 +41,8 @@ internal class ReflectionCache(reflectionCacheSize: Int) {
private val javaConstructorIsCreatorAnnotated = LRUMap<AnnotatedConstructor, Boolean>(reflectionCacheSize, reflectionCacheSize)
private val javaMemberIsRequired = LRUMap<AnnotatedMember, BooleanTriState?>(reflectionCacheSize, reflectionCacheSize)
private val kotlinGeneratedMethod = LRUMap<AnnotatedMethod, Boolean>(reflectionCacheSize, reflectionCacheSize)

private val javaConstructorToInstantiator = LRUMap<Constructor<Any>, ConstructorInstantiator<Any>>(reflectionCacheSize, reflectionCacheSize)
private val javaMethodToInstantiator = LRUMap<Method, MethodInstantiator<*>>(reflectionCacheSize, reflectionCacheSize)

fun kotlinFromJava(key: Class<Any>): KClass<Any> = javaClassToKotlin.get(key)
?: key.kotlin.let { javaClassToKotlin.putIfAbsent(key, it) ?: it }
Expand All @@ -57,4 +61,58 @@ internal class ReflectionCache(reflectionCacheSize: Int) {

fun isKotlinGeneratedMethod(key: AnnotatedMethod, calc: (AnnotatedMethod) -> Boolean): Boolean = kotlinGeneratedMethod.get(key)
?: calc(key).let { kotlinGeneratedMethod.putIfAbsent(key, it) ?: it }

private fun instantiatorFromJavaConstructor(key: Constructor<Any>): ConstructorInstantiator<*>? = javaConstructorToInstantiator.get(key)
?: kotlinFromJava(key)?.let {
val instantiator = ConstructorInstantiator(it, key)
javaConstructorToInstantiator.putIfAbsent(key, instantiator) ?: instantiator
}

private fun instantiatorFromJavaMethod(key: Method): MethodInstantiator<*>? = javaMethodToInstantiator.get(key)
?: kotlinFromJava(key)?.takeIf {
// we shouldn't have an instance or receiver parameter and if we do, just go with default Java-ish behavior
it.extensionReceiverParameter == null
}?.let { callable ->
var companionInstance: Any? = null
var companionAccessible: Boolean? = null

callable.instanceParameter!!.type.erasedType().kotlin
.takeIf { it.isCompanion } // abort, we have some unknown case here
?.let { possibleCompanion ->
try {
companionInstance = possibleCompanion.objectInstance
companionAccessible = true
} catch (ex: IllegalAccessException) {
// fallback for when an odd access exception happens through Kotlin reflection
possibleCompanion.java.enclosingClass.fields
.firstOrNull { it.type.kotlin.isCompanion }
?.let {
companionAccessible = it.isAccessible
it.isAccessible = true

companionInstance = it.get(null)
} ?: throw ex
}
}

companionInstance?.let {
MethodInstantiator(callable, key, it, companionAccessible!!).run {
javaMethodToInstantiator.putIfAbsent(key, this) ?: this
}
}
}

/*
* return null if...
* - can't get kotlinFunction
* - contains extensionReceiverParameter
* - instance parameter is not companion object or can't get
*/
@Suppress("UNCHECKED_CAST")
fun instantiatorFromJava(_withArgsCreator: AnnotatedWithParams): Instantiator<*>? = when (_withArgsCreator) {
is AnnotatedConstructor -> instantiatorFromJavaConstructor(_withArgsCreator.annotated as Constructor<Any>)
is AnnotatedMethod -> instantiatorFromJavaMethod(_withArgsCreator.annotated as Method)
else ->
throw IllegalStateException("Expected a constructor or method to create a Kotlin object, instead found ${_withArgsCreator.annotated.javaClass.name}")
}
}

This file was deleted.

0 comments on commit c320833

Please sign in to comment.