diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinModule.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinModule.kt index 3b2ac940..fc084a20 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinModule.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinModule.kt @@ -77,11 +77,9 @@ class KotlinModule constructor ( } val cache = ReflectionCache(reflectionCacheSize) - val cacheNew = ReflectionCacheNew(reflectionCacheSize) context.addValueInstantiators(KotlinInstantiators( cache, - cacheNew, nullToEmptyCollection, nullToEmptyMap, nullIsSameAsDefault, diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt index c25a5204..67074aad 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt @@ -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, @@ -38,7 +37,7 @@ internal class KotlinValueInstantiator( props: Array, 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() @@ -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, @@ -297,7 +295,6 @@ internal class KotlinInstantiators( KotlinValueInstantiator( defaultInstantiator, cache, - cacheNew, nullToEmptyCollection, nullToEmptyMap, nullIsSameAsDefault, 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 355a7e95..4da7acc0 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt @@ -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 @@ -38,7 +41,8 @@ internal class ReflectionCache(reflectionCacheSize: Int) { private val javaConstructorIsCreatorAnnotated = LRUMap(reflectionCacheSize, reflectionCacheSize) private val javaMemberIsRequired = LRUMap(reflectionCacheSize, reflectionCacheSize) private val kotlinGeneratedMethod = LRUMap(reflectionCacheSize, reflectionCacheSize) - + private val javaConstructorToInstantiator = LRUMap, ConstructorInstantiator>(reflectionCacheSize, reflectionCacheSize) + private val javaMethodToInstantiator = LRUMap>(reflectionCacheSize, reflectionCacheSize) fun kotlinFromJava(key: Class): KClass = javaClassToKotlin.get(key) ?: key.kotlin.let { javaClassToKotlin.putIfAbsent(key, it) ?: it } @@ -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): 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) + 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}") + } } diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCacheNew.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCacheNew.kt deleted file mode 100644 index ef178937..00000000 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCacheNew.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.fasterxml.jackson.module.kotlin - -import com.fasterxml.jackson.databind.introspect.AnnotatedConstructor -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.full.extensionReceiverParameter -import kotlin.reflect.full.instanceParameter -import kotlin.reflect.jvm.kotlinFunction - -// TODO: Consider whether it is necessary to include it in the ReflectionCache class. -internal class ReflectionCacheNew(reflectionCacheSize: Int) { - private val javaConstructorToKotlin = LRUMap, ConstructorInstantiator>(reflectionCacheSize, reflectionCacheSize) - private val javaMethodToKotlin = LRUMap>(reflectionCacheSize, reflectionCacheSize) - - private fun kotlinFromJava(key: Constructor): ConstructorInstantiator? = javaConstructorToKotlin.get(key) - ?: key.kotlinFunction?.let { - val instantiator = ConstructorInstantiator(it, key) - javaConstructorToKotlin.putIfAbsent(key, instantiator) ?: instantiator - } - - private fun kotlinFromJava(key: Method): MethodInstantiator<*>? = javaMethodToKotlin.get(key) - ?: key.kotlinFunction?.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 { - javaMethodToKotlin.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 -> kotlinFromJava(_withArgsCreator.annotated as Constructor) - is AnnotatedMethod -> kotlinFromJava(_withArgsCreator.annotated as Method) - else -> - throw IllegalStateException("Expected a constructor or method to create a Kotlin object, instead found ${_withArgsCreator.annotated.javaClass.name}") - } -}