@@ -9,7 +9,11 @@ import java.io.Serializable
9
9
import java.lang.reflect.Constructor
10
10
import java.lang.reflect.Executable
11
11
import java.lang.reflect.Method
12
+ import java.util.*
13
+ import kotlin.reflect.KClass
12
14
import kotlin.reflect.KFunction
15
+ import kotlin.reflect.full.memberProperties
16
+ import kotlin.reflect.jvm.javaGetter
13
17
import kotlin.reflect.jvm.kotlinFunction
14
18
15
19
internal class ReflectionCache (reflectionCacheSize : Int ) : Serializable {
@@ -44,6 +48,15 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
44
48
private val javaConstructorIsCreatorAnnotated = LRUMap <AnnotatedConstructor , Boolean >(reflectionCacheSize, reflectionCacheSize)
45
49
private val javaMemberIsRequired = LRUMap <AnnotatedMember , BooleanTriState ?>(reflectionCacheSize, reflectionCacheSize)
46
50
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
+
47
60
fun kotlinFromJava (key : Constructor <Any >): KFunction <Any >? = javaConstructorToKotlin.get(key)
48
61
? : key.kotlinFunction?.let { javaConstructorToKotlin.putIfAbsent(key, it) ? : it }
49
62
@@ -87,4 +100,44 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
87
100
88
101
fun javaMemberIsRequired (key : AnnotatedMember , calc : (AnnotatedMember ) -> Boolean? ): Boolean? = javaMemberIsRequired.get(key)?.value
89
102
? : 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
+ }
90
143
}
0 commit comments