From 1bc64c83bd5c17a4589b860cb11d8d4de301bdd2 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 04:59:39 +0900 Subject: [PATCH 01/26] Modified to require only minimal value as converter argument --- .../KotlinFallbackAnnotationIntrospector.kt | 6 +++--- .../jackson/module/kogera/deser/Converters.kt | 13 ++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt index 0d427f97..266374ee 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt @@ -189,11 +189,11 @@ private fun ValueParameter.isNullishTypeAt(index: Int) = arguments.getOrNull(ind private fun ValueParameter.createStrictNullChecksConverterOrNull(type: JavaType, rawType: Class<*>): Converter<*, *>? { return when { Array::class.java.isAssignableFrom(rawType) && !this.isNullishTypeAt(0) -> - CollectionValueStrictNullChecksConverter.ForArray(type, this) + CollectionValueStrictNullChecksConverter.ForArray(type, this.name) Iterable::class.java.isAssignableFrom(rawType) && !this.isNullishTypeAt(0) -> - CollectionValueStrictNullChecksConverter.ForIterable(type, this) + CollectionValueStrictNullChecksConverter.ForIterable(type, this.name) Map::class.java.isAssignableFrom(rawType) && !this.isNullishTypeAt(1) -> - MapValueStrictNullChecksConverter(type, this) + MapValueStrictNullChecksConverter(type, this.name) else -> null } } diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/Converters.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/Converters.kt index 006964a8..a5e821dc 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/Converters.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/Converters.kt @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.exc.MismatchedInputException import com.fasterxml.jackson.databind.type.TypeFactory import com.fasterxml.jackson.databind.util.Converter import com.fasterxml.jackson.databind.util.StdConverter -import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator.ValueParameter internal class ValueClassUnboxConverter(private val valueClass: Class) : StdConverter() { private val unboxMethod = valueClass.getDeclaredMethod("unbox-impl").apply { @@ -19,7 +18,7 @@ internal class ValueClassUnboxConverter(private val valueClass: Class : Converter { protected abstract val type: JavaType - protected abstract val valueParameter: ValueParameter + protected abstract val paramName: String protected abstract fun getValues(value: T): Iterator<*> @@ -29,7 +28,7 @@ internal sealed class CollectionValueStrictNullChecksConverter : Conver throw MismatchedInputException.from( null, null as JavaType?, - "A null value was entered for the parameter ${valueParameter.name}." + "A null value was entered for the parameter $paramName." ) } } @@ -42,14 +41,14 @@ internal sealed class CollectionValueStrictNullChecksConverter : Conver class ForIterable( override val type: JavaType, - override val valueParameter: ValueParameter + override val paramName: String ) : CollectionValueStrictNullChecksConverter>() { override fun getValues(value: Iterable<*>): Iterator<*> = value.iterator() } class ForArray constructor( override val type: JavaType, - override val valueParameter: ValueParameter + override val paramName: String ) : CollectionValueStrictNullChecksConverter>() { override fun getValues(value: Array<*>): Iterator<*> = value.iterator() } @@ -57,7 +56,7 @@ internal sealed class CollectionValueStrictNullChecksConverter : Conver internal class MapValueStrictNullChecksConverter( private val type: JavaType, - private val valueParameter: ValueParameter + private val paramName: String ) : Converter, Map<*, *>> { override fun convert(value: Map<*, *>): Map<*, *> = value.apply { entries.forEach { (k, v) -> @@ -65,7 +64,7 @@ internal class MapValueStrictNullChecksConverter( throw MismatchedInputException.from( null, null as JavaType?, - "A null value was entered for key $k of the parameter ${valueParameter.name}." + "A null value was entered for key $k of the parameter $paramName." ) } } From 31544da1d41ecb7d0692309204c61f709d2249a0 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 05:21:21 +0900 Subject: [PATCH 02/26] Suppress warn Because it is necessary to use the serialVersionUID as a unique name --- .../github/projectmapk/jackson/module/kogera/ReflectionCache.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt index ee364870..30f38060 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt @@ -16,6 +16,7 @@ import java.util.Optional internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { companion object { // Increment is required when properties that use LRUMap are changed. + @Suppress("ConstPropertyName") private const val serialVersionUID = 1L } From c0133329aff7426e2ae067ed9aa0a4f97357712b Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 13:36:05 +0900 Subject: [PATCH 03/26] Remove unused param --- .../io/github/projectmapk/jackson/module/kogera/KotlinModule.kt | 2 +- .../KotlinFallbackAnnotationIntrospector.kt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt index 709f5e84..dfa4d3ed 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt @@ -83,7 +83,7 @@ public class KotlinModule private constructor( context.insertAnnotationIntrospector( KotlinPrimaryAnnotationIntrospector(nullToEmptyCollection, nullToEmptyMap, cache) ) - context.appendAnnotationIntrospector(KotlinFallbackAnnotationIntrospector(this, strictNullChecks, cache)) + context.appendAnnotationIntrospector(KotlinFallbackAnnotationIntrospector(strictNullChecks, cache)) context.setClassIntrospector(KotlinClassIntrospector) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt index 266374ee..3a98f11d 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt @@ -37,7 +37,6 @@ import java.lang.reflect.Modifier // (in most cases, JacksonAnnotationIntrospector). // Original name: KotlinNamesAnnotationIntrospector internal class KotlinFallbackAnnotationIntrospector( - val module: KotlinModule, private val strictNullChecks: Boolean, private val cache: ReflectionCache ) : NopAnnotationIntrospector() { From 1b9ebc283f239464a95d67662a4b9658c25687ce Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 13:37:45 +0900 Subject: [PATCH 04/26] Fix test --- .../module/kogera/_ported/test/ObjectSingletonTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_ported/test/ObjectSingletonTest.kt b/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_ported/test/ObjectSingletonTest.kt index a57fded0..a8fad872 100644 --- a/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_ported/test/ObjectSingletonTest.kt +++ b/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_ported/test/ObjectSingletonTest.kt @@ -44,17 +44,17 @@ class TestObjectSingleton { // read back persisted state resets singleton state val newSingleton = mapper.readValue(js) assertEquals(initial, Singleton.content) - assertEquals(initial, Singleton.content) + assertEquals(initial, newSingleton.content) } @Test fun deserializedObjectsBehaveLikeSingletons() { val js = mapper.writeValueAsString(Singleton) val newSingleton = mapper.readValue(js) - assertEquals(Singleton.content, Singleton.content) + assertEquals(Singleton.content, newSingleton.content) Singleton.content += 1 - assertEquals(Singleton.content, Singleton.content) + assertEquals(Singleton.content, newSingleton.content) } } From 6238514a74a2d6df258edff0c283edcecaa81dc2 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 13:42:45 +0900 Subject: [PATCH 05/26] Formatting --- .../KotlinFallbackAnnotationIntrospector.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt index 3a98f11d..0159c2da 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt @@ -12,7 +12,6 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedParameter import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector import com.fasterxml.jackson.databind.type.TypeFactory import com.fasterxml.jackson.databind.util.Converter -import io.github.projectmapk.jackson.module.kogera.KotlinModule import io.github.projectmapk.jackson.module.kogera.ReflectionCache import io.github.projectmapk.jackson.module.kogera.deser.CollectionValueStrictNullChecksConverter import io.github.projectmapk.jackson.module.kogera.deser.MapValueStrictNullChecksConverter From 7a90e8e7c4f2556cdd298f040e6299cfc7c93f74 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 13:55:42 +0900 Subject: [PATCH 06/26] Add Jackson Metadata class temp --- .../projectmapk/jackson/module/kogera/JmClass.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt new file mode 100644 index 00000000..d9edfb0b --- /dev/null +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt @@ -0,0 +1,10 @@ +package io.github.projectmapk.jackson.module.kogera + +import kotlinx.metadata.KmClass + +// Jackson Metadata Class +internal class JmClass(val kmClass: KmClass) { + companion object { + fun createOrNull(clazz: Class<*>): JmClass? = clazz.toKmClass()?.let { JmClass(it) } + } +} From f4b0b7fdd8c9671319407115d9e9dbe5078900fe Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:04:30 +0900 Subject: [PATCH 07/26] Temp rename --- .../jackson/module/kogera/ReflectionCache.kt | 8 ++++---- .../KotlinFallbackAnnotationIntrospector.kt | 16 ++++++++-------- .../KotlinPrimaryAnnotationIntrospector.kt | 8 ++++---- .../deser/deserializers/KotlinDeserializers.kt | 2 +- .../jackson/module/kogera/ReflectionCacheTest.kt | 6 +++--- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt index 30f38060..c47cb5d4 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt @@ -48,7 +48,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { creatorCache = LRUMap(initialEntries, reflectionCacheSize) } - fun getKmClass(clazz: Class<*>): KmClass? { + fun getJmClass(clazz: Class<*>): KmClass? { val optional = classCache.get(clazz) return if (optional != null) { @@ -66,7 +66,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { is Constructor<*> -> { creatorCache.get(creator) ?: run { - getKmClass(creator.declaringClass)?.let { + getJmClass(creator.declaringClass)?.let { val value = ConstructorValueCreator(creator, it) creatorCache.putIfAbsent(creator, value) ?: value } @@ -76,7 +76,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { is Method -> { creatorCache.get(creator) ?: run { - getKmClass(creator.declaringClass)?.let { + getJmClass(creator.declaringClass)?.let { val value = MethodValueCreator(creator, it) creatorCache.putIfAbsent(creator, value) ?: value } @@ -95,7 +95,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { // TODO: Verify the case where a value class encompasses another value class. if (this.returnType.isUnboxableValueClass()) return null } - val kotlinProperty = getKmClass(getter.declaringClass)?.findPropertyByGetter(getter) + val kotlinProperty = getJmClass(getter.declaringClass)?.findPropertyByGetter(getter) // Since there was no way to directly determine whether returnType is a value class or not, // Class is restored and processed. diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt index 0159c2da..b0613655 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt @@ -42,7 +42,7 @@ internal class KotlinFallbackAnnotationIntrospector( // since 2.4 override fun findImplicitPropertyName(member: AnnotatedMember): String? = when (member) { is AnnotatedMethod -> if (member.parameterCount == 0) { - cache.getKmClass(member.declaringClass)?.findPropertyByGetter(member.annotated)?.name + cache.getJmClass(member.declaringClass)?.findPropertyByGetter(member.annotated)?.name } else { null } @@ -51,14 +51,14 @@ internal class KotlinFallbackAnnotationIntrospector( } private fun findKotlinParameterName(param: AnnotatedParameter): String? = when (val owner = param.owner.member) { - is Constructor<*> -> cache.getKmClass(param.declaringClass)?.findKmConstructor(owner)?.valueParameters + is Constructor<*> -> cache.getJmClass(param.declaringClass)?.findKmConstructor(owner)?.valueParameters is Method -> owner.takeIf { _ -> Modifier.isStatic(owner.modifiers) } ?.let { _ -> - val companion = cache.getKmClass(param.declaringClass)?.companionObject ?: return@let null + val companion = cache.getJmClass(param.declaringClass)?.companionObject ?: return@let null val companionKmClass = owner.declaringClass.getDeclaredField(companion) .type - .let { cache.getKmClass(it) }!! + .let { cache.getJmClass(it) }!! val signature = owner.toSignature() companionKmClass.functions.find { it.signature == signature }?.valueParameters @@ -68,15 +68,15 @@ internal class KotlinFallbackAnnotationIntrospector( // If it is not a property on Kotlin, it is not used to ser/deserialization override fun findPropertyAccess(ann: Annotated): JsonProperty.Access? = (ann as? AnnotatedMethod)?.let { _ -> - cache.getKmClass(ann.declaringClass)?.let { kmClass -> + cache.getJmClass(ann.declaringClass)?.let { jmClass -> val method = ann.annotated // By returning an illegal JsonProperty.Access, it is effectively ignore. when (method.parameters.size) { - 0 -> JsonProperty.Access.WRITE_ONLY.takeIf { kmClass.findPropertyByGetter(method) == null } + 0 -> JsonProperty.Access.WRITE_ONLY.takeIf { jmClass.findPropertyByGetter(method) == null } 1 -> { val signature = method.toSignature() - JsonProperty.Access.READ_ONLY.takeIf { kmClass.properties.none { it.setterSignature == signature } } + JsonProperty.Access.READ_ONLY.takeIf { jmClass.properties.none { it.setterSignature == signature } } } else -> null } @@ -115,7 +115,7 @@ internal class KotlinFallbackAnnotationIntrospector( // Determine if the unbox result of value class is nullable // @see findNullSerializer private fun Class<*>.requireRebox(): Boolean = - cache.getKmClass(this)!!.properties.first { it.fieldSignature != null }.returnType.isNullable() + cache.getJmClass(this)!!.properties.first { it.fieldSignature != null }.returnType.isNullable() // 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. diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt index 472593d3..1783203e 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt @@ -46,7 +46,7 @@ internal class KotlinPrimaryAnnotationIntrospector( val byAnnotation = _findAnnotation(m, JsonProperty::class.java)?.required ?.apply { if (this) return true } - return cache.getKmClass(m.member.declaringClass)?.let { + return cache.getJmClass(m.member.declaringClass)?.let { when (m) { is AnnotatedField -> m.hasRequiredMarker(it) is AnnotatedMethod -> m.getRequiredMarkerFromCorrespondingAccessor(it) @@ -120,8 +120,8 @@ internal class KotlinPrimaryAnnotationIntrospector( */ // The definition location was not changed from kotlin-module because // the result was the same whether it was defined in Primary or Fallback. - override fun findSubtypes(a: Annotated): List? = cache.getKmClass(a.rawType)?.let { kmClass -> - kmClass.sealedSubclasses.map { NamedType(it.reconstructClass()) }.ifEmpty { null } + override fun findSubtypes(a: Annotated): List? = cache.getJmClass(a.rawType)?.let { jmClass -> + jmClass.sealedSubclasses.map { NamedType(it.reconstructClass()) }.ifEmpty { null } } // Return Mode.DEFAULT if ann is a Primary Constructor and the condition is satisfied. @@ -136,7 +136,7 @@ internal class KotlinPrimaryAnnotationIntrospector( val declaringClass = ann.declaringClass val kmClass = declaringClass ?.takeIf { !it.isEnum } - ?.let { cache.getKmClass(it) } + ?.let { cache.getJmClass(it) } ?: return null return JsonCreator.Mode.DEFAULT diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt index c46ad1db..c594ab69 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt @@ -142,7 +142,7 @@ internal class KotlinDeserializers(private val cache: ReflectionCache) : Deseria rawClass == UShort::class.java -> UShortDeserializer rawClass == UInt::class.java -> UIntDeserializer rawClass == ULong::class.java -> ULongDeserializer - rawClass.isUnboxableValueClass() -> findValueCreator(type, rawClass, cache.getKmClass(rawClass)!!) + rawClass.isUnboxableValueClass() -> findValueCreator(type, rawClass, cache.getJmClass(rawClass)!!) ?.let { ValueClassBoxDeserializer(it, rawClass) } else -> null } diff --git a/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCacheTest.kt b/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCacheTest.kt index 6e7adbc4..4718b42a 100644 --- a/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCacheTest.kt +++ b/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCacheTest.kt @@ -18,13 +18,13 @@ class ReflectionCacheTest { assertNotNull(deserialized) // Deserialized instance also do not raise exceptions - deserialized.getKmClass(this::class.java) + deserialized.getJmClass(this::class.java) } } @Test fun notEmptyCache() { - val cache = ReflectionCache(100).apply { getKmClass(this::class.java) } + val cache = ReflectionCache(100).apply { getJmClass(this::class.java) } val serialized = jdkSerialize(cache) assertDoesNotThrow { @@ -32,7 +32,7 @@ class ReflectionCacheTest { assertNotNull(deserialized) // Deserialized instance also do not raise exceptions - deserialized.getKmClass(this::class.java) + deserialized.getJmClass(this::class.java) } } } From 524aed1807a48b9cbaa15459b701266d97bd5259 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:07:14 +0900 Subject: [PATCH 08/26] Fix to return JmClass temp --- .../jackson/module/kogera/ReflectionCache.kt | 13 ++++++------- .../KotlinFallbackAnnotationIntrospector.kt | 14 +++++++------- .../KotlinPrimaryAnnotationIntrospector.kt | 10 +++++----- .../deser/deserializers/KotlinDeserializers.kt | 2 +- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt index c47cb5d4..eb4dfda9 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt @@ -6,7 +6,6 @@ import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.crea import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator.MethodValueCreator import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator.ValueCreator import io.github.projectmapk.jackson.module.kogera.ser.ValueClassBoxConverter -import kotlinx.metadata.KmClass import java.io.Serializable import java.lang.reflect.Constructor import java.lang.reflect.Executable @@ -21,7 +20,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { } // This cache is used for both serialization and deserialization, so reserve a larger size from the start. - private val classCache = LRUMap, Optional>(reflectionCacheSize, reflectionCacheSize) + private val classCache = LRUMap, Optional>(reflectionCacheSize, reflectionCacheSize) private val creatorCache: LRUMap> // Initial size is 0 because the value class is not always used @@ -48,13 +47,13 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { creatorCache = LRUMap(initialEntries, reflectionCacheSize) } - fun getJmClass(clazz: Class<*>): KmClass? { + fun getJmClass(clazz: Class<*>): JmClass? { val optional = classCache.get(clazz) return if (optional != null) { optional } else { - val value = Optional.ofNullable(clazz.toKmClass()) + val value = Optional.ofNullable(JmClass.createOrNull(clazz)) (classCache.putIfAbsent(clazz, value) ?: value) }.orElse(null) } @@ -67,7 +66,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { creatorCache.get(creator) ?: run { getJmClass(creator.declaringClass)?.let { - val value = ConstructorValueCreator(creator, it) + val value = ConstructorValueCreator(creator, it.kmClass) creatorCache.putIfAbsent(creator, value) ?: value } } @@ -77,7 +76,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { creatorCache.get(creator) ?: run { getJmClass(creator.declaringClass)?.let { - val value = MethodValueCreator(creator, it) + val value = MethodValueCreator(creator, it.kmClass) creatorCache.putIfAbsent(creator, value) ?: value } } @@ -95,7 +94,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { // 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) + val kotlinProperty = getJmClass(getter.declaringClass)?.kmClass?.findPropertyByGetter(getter) // Since there was no way to directly determine whether returnType is a value class or not, // Class is restored and processed. diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt index b0613655..0815b5bc 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt @@ -42,7 +42,7 @@ internal class KotlinFallbackAnnotationIntrospector( // since 2.4 override fun findImplicitPropertyName(member: AnnotatedMember): String? = when (member) { is AnnotatedMethod -> if (member.parameterCount == 0) { - cache.getJmClass(member.declaringClass)?.findPropertyByGetter(member.annotated)?.name + cache.getJmClass(member.declaringClass)?.kmClass?.findPropertyByGetter(member.annotated)?.name } else { null } @@ -51,17 +51,17 @@ internal class KotlinFallbackAnnotationIntrospector( } private fun findKotlinParameterName(param: AnnotatedParameter): String? = when (val owner = param.owner.member) { - is Constructor<*> -> cache.getJmClass(param.declaringClass)?.findKmConstructor(owner)?.valueParameters + is Constructor<*> -> cache.getJmClass(param.declaringClass)?.kmClass?.findKmConstructor(owner)?.valueParameters is Method -> owner.takeIf { _ -> Modifier.isStatic(owner.modifiers) } ?.let { _ -> - val companion = cache.getJmClass(param.declaringClass)?.companionObject ?: return@let null + val companion = cache.getJmClass(param.declaringClass)?.kmClass?.companionObject ?: return@let null val companionKmClass = owner.declaringClass.getDeclaredField(companion) .type .let { cache.getJmClass(it) }!! val signature = owner.toSignature() - companionKmClass.functions.find { it.signature == signature }?.valueParameters + companionKmClass.kmClass.functions.find { it.signature == signature }?.valueParameters } else -> null }?.let { it[param.index].name } @@ -73,10 +73,10 @@ internal class KotlinFallbackAnnotationIntrospector( // By returning an illegal JsonProperty.Access, it is effectively ignore. when (method.parameters.size) { - 0 -> JsonProperty.Access.WRITE_ONLY.takeIf { jmClass.findPropertyByGetter(method) == null } + 0 -> JsonProperty.Access.WRITE_ONLY.takeIf { jmClass.kmClass.findPropertyByGetter(method) == null } 1 -> { val signature = method.toSignature() - JsonProperty.Access.READ_ONLY.takeIf { jmClass.properties.none { it.setterSignature == signature } } + JsonProperty.Access.READ_ONLY.takeIf { jmClass.kmClass.properties.none { it.setterSignature == signature } } } else -> null } @@ -115,7 +115,7 @@ internal class KotlinFallbackAnnotationIntrospector( // Determine if the unbox result of value class is nullable // @see findNullSerializer private fun Class<*>.requireRebox(): Boolean = - cache.getJmClass(this)!!.properties.first { it.fieldSignature != null }.returnType.isNullable() + cache.getJmClass(this)!!.kmClass.properties.first { it.fieldSignature != null }.returnType.isNullable() // 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. diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt index 1783203e..b778ebb0 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt @@ -48,9 +48,9 @@ internal class KotlinPrimaryAnnotationIntrospector( return cache.getJmClass(m.member.declaringClass)?.let { when (m) { - is AnnotatedField -> m.hasRequiredMarker(it) - is AnnotatedMethod -> m.getRequiredMarkerFromCorrespondingAccessor(it) - is AnnotatedParameter -> m.hasRequiredMarker(it) + is AnnotatedField -> m.hasRequiredMarker(it.kmClass) + is AnnotatedMethod -> m.getRequiredMarkerFromCorrespondingAccessor(it.kmClass) + is AnnotatedParameter -> m.hasRequiredMarker(it.kmClass) else -> null } } ?: byAnnotation // If a JsonProperty is available, use it to reduce processing costs. @@ -121,7 +121,7 @@ internal class KotlinPrimaryAnnotationIntrospector( // The definition location was not changed from kotlin-module because // the result was the same whether it was defined in Primary or Fallback. override fun findSubtypes(a: Annotated): List? = cache.getJmClass(a.rawType)?.let { jmClass -> - jmClass.sealedSubclasses.map { NamedType(it.reconstructClass()) }.ifEmpty { null } + jmClass.kmClass.sealedSubclasses.map { NamedType(it.reconstructClass()) }.ifEmpty { null } } // Return Mode.DEFAULT if ann is a Primary Constructor and the condition is satisfied. @@ -140,7 +140,7 @@ internal class KotlinPrimaryAnnotationIntrospector( ?: return null return JsonCreator.Mode.DEFAULT - .takeIf { ann.annotated.isPrimarilyConstructorOf(kmClass) && !hasCreator(declaringClass, kmClass) } + .takeIf { ann.annotated.isPrimarilyConstructorOf(kmClass.kmClass) && !hasCreator(declaringClass, kmClass.kmClass) } } } diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt index c594ab69..12e4ef1a 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt @@ -142,7 +142,7 @@ internal class KotlinDeserializers(private val cache: ReflectionCache) : Deseria rawClass == UShort::class.java -> UShortDeserializer rawClass == UInt::class.java -> UIntDeserializer rawClass == ULong::class.java -> ULongDeserializer - rawClass.isUnboxableValueClass() -> findValueCreator(type, rawClass, cache.getJmClass(rawClass)!!) + rawClass.isUnboxableValueClass() -> findValueCreator(type, rawClass, cache.getJmClass(rawClass)!!.kmClass) ?.let { ValueClassBoxDeserializer(it, rawClass) } else -> null } From c26e2178c7272f47c93677a58bac1807fe517730 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:10:03 +0900 Subject: [PATCH 09/26] Implement properties --- .../io/github/projectmapk/jackson/module/kogera/JmClass.kt | 3 +++ .../KotlinFallbackAnnotationIntrospector.kt | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt index d9edfb0b..44de82a3 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt @@ -1,9 +1,12 @@ package io.github.projectmapk.jackson.module.kogera import kotlinx.metadata.KmClass +import kotlinx.metadata.KmProperty // Jackson Metadata Class internal class JmClass(val kmClass: KmClass) { + val properties: List = kmClass.properties + companion object { fun createOrNull(clazz: Class<*>): JmClass? = clazz.toKmClass()?.let { JmClass(it) } } diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt index 0815b5bc..7c2ce609 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt @@ -76,7 +76,7 @@ internal class KotlinFallbackAnnotationIntrospector( 0 -> JsonProperty.Access.WRITE_ONLY.takeIf { jmClass.kmClass.findPropertyByGetter(method) == null } 1 -> { val signature = method.toSignature() - JsonProperty.Access.READ_ONLY.takeIf { jmClass.kmClass.properties.none { it.setterSignature == signature } } + JsonProperty.Access.READ_ONLY.takeIf { jmClass.properties.none { it.setterSignature == signature } } } else -> null } @@ -115,7 +115,7 @@ internal class KotlinFallbackAnnotationIntrospector( // Determine if the unbox result of value class is nullable // @see findNullSerializer private fun Class<*>.requireRebox(): Boolean = - cache.getJmClass(this)!!.kmClass.properties.first { it.fieldSignature != null }.returnType.isNullable() + cache.getJmClass(this)!!.properties.first { it.fieldSignature != null }.returnType.isNullable() // 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. From 2238602ae2ae9de74d4a236523071c0c0a8227d3 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:11:41 +0900 Subject: [PATCH 10/26] Impl findPropertyByGetter --- .../projectmapk/jackson/module/kogera/InternalCommons.kt | 7 ------- .../io/github/projectmapk/jackson/module/kogera/JmClass.kt | 7 +++++++ .../projectmapk/jackson/module/kogera/ReflectionCache.kt | 2 +- .../KotlinFallbackAnnotationIntrospector.kt | 5 ++--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt index 0042754e..2f01205b 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt @@ -5,13 +5,11 @@ import kotlinx.metadata.Flag import kotlinx.metadata.KmClass import kotlinx.metadata.KmClassifier import kotlinx.metadata.KmConstructor -import kotlinx.metadata.KmProperty import kotlinx.metadata.KmType import kotlinx.metadata.KmValueParameter import kotlinx.metadata.jvm.JvmFieldSignature import kotlinx.metadata.jvm.JvmMethodSignature import kotlinx.metadata.jvm.KotlinClassMetadata -import kotlinx.metadata.jvm.getterSignature import kotlinx.metadata.jvm.signature import java.lang.reflect.AnnotatedElement import java.lang.reflect.Constructor @@ -126,11 +124,6 @@ internal fun KmClass.findKmConstructor(constructor: Constructor<*>): KmConstruct } } -internal fun KmClass.findPropertyByGetter(getter: Method): KmProperty? { - val signature = getter.toSignature() - return properties.find { it.getterSignature == signature } -} - internal fun KmType.isNullable(): Boolean = Flag.Type.IS_NULLABLE(this.flags) internal fun AnnotatedElement.hasCreatorAnnotation(): Boolean = diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt index 44de82a3..290dc863 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt @@ -2,11 +2,18 @@ package io.github.projectmapk.jackson.module.kogera import kotlinx.metadata.KmClass import kotlinx.metadata.KmProperty +import kotlinx.metadata.jvm.getterSignature +import java.lang.reflect.Method // Jackson Metadata Class internal class JmClass(val kmClass: KmClass) { val properties: List = kmClass.properties + fun findPropertyByGetter(getter: Method): KmProperty? { + val signature = getter.toSignature() + return properties.find { it.getterSignature == signature } + } + companion object { fun createOrNull(clazz: Class<*>): JmClass? = clazz.toKmClass()?.let { JmClass(it) } } diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt index eb4dfda9..dee4d832 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt @@ -94,7 +94,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { // TODO: Verify the case where a value class encompasses another value class. if (this.returnType.isUnboxableValueClass()) return null } - val kotlinProperty = getJmClass(getter.declaringClass)?.kmClass?.findPropertyByGetter(getter) + val kotlinProperty = getJmClass(getter.declaringClass)?.findPropertyByGetter(getter) // Since there was no way to directly determine whether returnType is a value class or not, // Class is restored and processed. diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt index 7c2ce609..cd26a032 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt @@ -18,7 +18,6 @@ import io.github.projectmapk.jackson.module.kogera.deser.MapValueStrictNullCheck import io.github.projectmapk.jackson.module.kogera.deser.ValueClassUnboxConverter import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator.ValueParameter import io.github.projectmapk.jackson.module.kogera.findKmConstructor -import io.github.projectmapk.jackson.module.kogera.findPropertyByGetter import io.github.projectmapk.jackson.module.kogera.isNullable import io.github.projectmapk.jackson.module.kogera.isUnboxableValueClass import io.github.projectmapk.jackson.module.kogera.reconstructClassOrNull @@ -42,7 +41,7 @@ internal class KotlinFallbackAnnotationIntrospector( // since 2.4 override fun findImplicitPropertyName(member: AnnotatedMember): String? = when (member) { is AnnotatedMethod -> if (member.parameterCount == 0) { - cache.getJmClass(member.declaringClass)?.kmClass?.findPropertyByGetter(member.annotated)?.name + cache.getJmClass(member.declaringClass)?.findPropertyByGetter(member.annotated)?.name } else { null } @@ -73,7 +72,7 @@ internal class KotlinFallbackAnnotationIntrospector( // By returning an illegal JsonProperty.Access, it is effectively ignore. when (method.parameters.size) { - 0 -> JsonProperty.Access.WRITE_ONLY.takeIf { jmClass.kmClass.findPropertyByGetter(method) == null } + 0 -> JsonProperty.Access.WRITE_ONLY.takeIf { jmClass.findPropertyByGetter(method) == null } 1 -> { val signature = method.toSignature() JsonProperty.Access.READ_ONLY.takeIf { jmClass.properties.none { it.setterSignature == signature } } From 8d57309a5c35657e154659908b983f526e374b6f Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:16:44 +0900 Subject: [PATCH 11/26] Impl constructors and findKmConstructor --- .../jackson/module/kogera/InternalCommons.kt | 22 ----------------- .../jackson/module/kogera/JmClass.kt | 24 +++++++++++++++++++ .../jackson/module/kogera/ReflectionCache.kt | 2 +- .../KotlinFallbackAnnotationIntrospector.kt | 3 +-- .../KotlinPrimaryAnnotationIntrospector.kt | 18 +++++++------- .../creator/ConstructorValueCreator.kt | 7 +++--- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt index 2f01205b..cae722e4 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt @@ -4,13 +4,11 @@ import com.fasterxml.jackson.annotation.JsonCreator import kotlinx.metadata.Flag import kotlinx.metadata.KmClass import kotlinx.metadata.KmClassifier -import kotlinx.metadata.KmConstructor import kotlinx.metadata.KmType import kotlinx.metadata.KmValueParameter import kotlinx.metadata.jvm.JvmFieldSignature import kotlinx.metadata.jvm.JvmMethodSignature import kotlinx.metadata.jvm.KotlinClassMetadata -import kotlinx.metadata.jvm.signature import java.lang.reflect.AnnotatedElement import java.lang.reflect.Constructor import java.lang.reflect.Field @@ -104,26 +102,6 @@ internal fun String.reconstructClass(): Class<*> { internal fun KmType.reconstructClassOrNull(): Class<*>? = (classifier as? KmClassifier.Class) ?.let { kotlin.runCatching { it.name.reconstructClass() }.getOrNull() } -internal fun KmClass.findKmConstructor(constructor: Constructor<*>): KmConstructor? { - val descHead = constructor.parameterTypes.toDescBuilder() - val desc = CharArray(descHead.length + 1).apply { - descHead.getChars(0, descHead.length, this, 0) - this[this.lastIndex] = 'V' - }.let { String(it) } - - // Only constructors that take a value class as an argument have a DefaultConstructorMarker on the Signature. - val valueDesc = descHead - .deleteCharAt(descHead.length - 1) - .append("Lkotlin/jvm/internal/DefaultConstructorMarker;)V") - .toString() - - // Constructors always have the same name, so only desc is compared - return constructors.find { - val targetDesc = it.signature?.desc - targetDesc == desc || targetDesc == valueDesc - } -} - internal fun KmType.isNullable(): Boolean = Flag.Type.IS_NULLABLE(this.flags) internal fun AnnotatedElement.hasCreatorAnnotation(): Boolean = diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt index 290dc863..d76f34e6 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt @@ -1,14 +1,38 @@ package io.github.projectmapk.jackson.module.kogera import kotlinx.metadata.KmClass +import kotlinx.metadata.KmConstructor import kotlinx.metadata.KmProperty import kotlinx.metadata.jvm.getterSignature +import kotlinx.metadata.jvm.signature +import java.lang.reflect.Constructor import java.lang.reflect.Method // Jackson Metadata Class internal class JmClass(val kmClass: KmClass) { + val constructors: List = kmClass.constructors val properties: List = kmClass.properties + fun findKmConstructor(constructor: Constructor<*>): KmConstructor? { + val descHead = constructor.parameterTypes.toDescBuilder() + val desc = CharArray(descHead.length + 1).apply { + descHead.getChars(0, descHead.length, this, 0) + this[this.lastIndex] = 'V' + }.let { String(it) } + + // Only constructors that take a value class as an argument have a DefaultConstructorMarker on the Signature. + val valueDesc = descHead + .deleteCharAt(descHead.length - 1) + .append("Lkotlin/jvm/internal/DefaultConstructorMarker;)V") + .toString() + + // Constructors always have the same name, so only desc is compared + return constructors.find { + val targetDesc = it.signature?.desc + targetDesc == desc || targetDesc == valueDesc + } + } + fun findPropertyByGetter(getter: Method): KmProperty? { val signature = getter.toSignature() return properties.find { it.getterSignature == signature } diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt index dee4d832..f5f7e3db 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt @@ -66,7 +66,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { creatorCache.get(creator) ?: run { getJmClass(creator.declaringClass)?.let { - val value = ConstructorValueCreator(creator, it.kmClass) + val value = ConstructorValueCreator(creator, it) creatorCache.putIfAbsent(creator, value) ?: value } } diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt index cd26a032..e77ee914 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt @@ -17,7 +17,6 @@ import io.github.projectmapk.jackson.module.kogera.deser.CollectionValueStrictNu import io.github.projectmapk.jackson.module.kogera.deser.MapValueStrictNullChecksConverter import io.github.projectmapk.jackson.module.kogera.deser.ValueClassUnboxConverter import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator.ValueParameter -import io.github.projectmapk.jackson.module.kogera.findKmConstructor import io.github.projectmapk.jackson.module.kogera.isNullable import io.github.projectmapk.jackson.module.kogera.isUnboxableValueClass import io.github.projectmapk.jackson.module.kogera.reconstructClassOrNull @@ -50,7 +49,7 @@ internal class KotlinFallbackAnnotationIntrospector( } private fun findKotlinParameterName(param: AnnotatedParameter): String? = when (val owner = param.owner.member) { - is Constructor<*> -> cache.getJmClass(param.declaringClass)?.kmClass?.findKmConstructor(owner)?.valueParameters + is Constructor<*> -> cache.getJmClass(param.declaringClass)?.findKmConstructor(owner)?.valueParameters is Method -> owner.takeIf { _ -> Modifier.isStatic(owner.modifiers) } ?.let { _ -> diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt index b778ebb0..d5efcfde 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt @@ -12,8 +12,8 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedMethod import com.fasterxml.jackson.databind.introspect.AnnotatedParameter import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector import com.fasterxml.jackson.databind.jsontype.NamedType +import io.github.projectmapk.jackson.module.kogera.JmClass import io.github.projectmapk.jackson.module.kogera.ReflectionCache -import io.github.projectmapk.jackson.module.kogera.findKmConstructor import io.github.projectmapk.jackson.module.kogera.hasCreatorAnnotation import io.github.projectmapk.jackson.module.kogera.isNullable import io.github.projectmapk.jackson.module.kogera.reconstructClass @@ -50,7 +50,7 @@ internal class KotlinPrimaryAnnotationIntrospector( when (m) { is AnnotatedField -> m.hasRequiredMarker(it.kmClass) is AnnotatedMethod -> m.getRequiredMarkerFromCorrespondingAccessor(it.kmClass) - is AnnotatedParameter -> m.hasRequiredMarker(it.kmClass) + is AnnotatedParameter -> m.hasRequiredMarker(it) else -> null } } ?: byAnnotation // If a JsonProperty is available, use it to reduce processing costs. @@ -90,13 +90,13 @@ internal class KotlinPrimaryAnnotationIntrospector( ?.isRequiredByNullability() } - private fun AnnotatedParameter.hasRequiredMarker(kmClass: KmClass): Boolean? { + private fun AnnotatedParameter.hasRequiredMarker(jmClass: JmClass): Boolean? { val paramDef = when (val member = member) { - is Constructor<*> -> kmClass.findKmConstructor(member) + is Constructor<*> -> jmClass.findKmConstructor(member) ?.let { it.valueParameters[index] } is Method -> { val signature = member.toSignature() - kmClass.functions.find { it.signature == signature } + jmClass.kmClass.functions.find { it.signature == signature } ?.let { it.valueParameters[index] } } else -> null @@ -134,18 +134,18 @@ internal class KotlinPrimaryAnnotationIntrospector( (ann as? AnnotatedConstructor)?.takeIf { 0 < it.parameterCount } ?: return null val declaringClass = ann.declaringClass - val kmClass = declaringClass + val jmClass = declaringClass ?.takeIf { !it.isEnum } ?.let { cache.getJmClass(it) } ?: return null return JsonCreator.Mode.DEFAULT - .takeIf { ann.annotated.isPrimarilyConstructorOf(kmClass.kmClass) && !hasCreator(declaringClass, kmClass.kmClass) } + .takeIf { ann.annotated.isPrimarilyConstructorOf(jmClass) && !hasCreator(declaringClass, jmClass.kmClass) } } } -private fun Constructor<*>.isPrimarilyConstructorOf(kmClass: KmClass): Boolean = kmClass.findKmConstructor(this) - ?.let { !Flag.Constructor.IS_SECONDARY(it.flags) || kmClass.constructors.size == 1 } +private fun Constructor<*>.isPrimarilyConstructorOf(jmClass: JmClass): Boolean = jmClass.findKmConstructor(this) + ?.let { !Flag.Constructor.IS_SECONDARY(it.flags) || jmClass.constructors.size == 1 } ?: false private fun KmClassifier.isString(): Boolean = this is KmClassifier.Class && this.name == "kotlin/String" diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/value_instantiator/creator/ConstructorValueCreator.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/value_instantiator/creator/ConstructorValueCreator.kt index 8476eef0..adac4eee 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/value_instantiator/creator/ConstructorValueCreator.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/value_instantiator/creator/ConstructorValueCreator.kt @@ -1,19 +1,18 @@ package io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator +import io.github.projectmapk.jackson.module.kogera.JmClass import io.github.projectmapk.jackson.module.kogera.call import io.github.projectmapk.jackson.module.kogera.defaultConstructorMarker import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.argument_bucket.ArgumentBucket import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.argument_bucket.BucketGenerator import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.calcMaskSize -import io.github.projectmapk.jackson.module.kogera.findKmConstructor import io.github.projectmapk.jackson.module.kogera.getDeclaredConstructorBy import io.github.projectmapk.jackson.module.kogera.hasVarargParam -import kotlinx.metadata.KmClass import java.lang.reflect.Constructor internal class ConstructorValueCreator( private val constructor: Constructor, - declaringKmClass: KmClass + declaringJmClass: JmClass ) : ValueCreator() { private val declaringClass: Class = constructor.declaringClass @@ -26,7 +25,7 @@ internal class ConstructorValueCreator( // To prevent the call from failing, save the initial value and then rewrite the flag. if (!isAccessible) constructor.isAccessible = true - val constructorParameters = declaringKmClass.findKmConstructor(constructor)!!.valueParameters + val constructorParameters = declaringJmClass.findKmConstructor(constructor)!!.valueParameters valueParameters = constructorParameters.map { ValueParameter(it) } bucketGenerator = BucketGenerator(constructor.parameterTypes.asList(), constructorParameters.hasVarargParam()) From ab7fde732ce5944575e828a35e519633f7dfe438 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:20:43 +0900 Subject: [PATCH 12/26] Impl findFunctionByMethod --- .../projectmapk/jackson/module/kogera/JmClass.kt | 7 +++++++ .../KotlinPrimaryAnnotationIntrospector.kt | 12 ++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt index d76f34e6..34131aad 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt @@ -2,6 +2,7 @@ package io.github.projectmapk.jackson.module.kogera import kotlinx.metadata.KmClass import kotlinx.metadata.KmConstructor +import kotlinx.metadata.KmFunction import kotlinx.metadata.KmProperty import kotlinx.metadata.jvm.getterSignature import kotlinx.metadata.jvm.signature @@ -12,6 +13,7 @@ import java.lang.reflect.Method internal class JmClass(val kmClass: KmClass) { val constructors: List = kmClass.constructors val properties: List = kmClass.properties + private val functions: List = kmClass.functions fun findKmConstructor(constructor: Constructor<*>): KmConstructor? { val descHead = constructor.parameterTypes.toDescBuilder() @@ -38,6 +40,11 @@ internal class JmClass(val kmClass: KmClass) { return properties.find { it.getterSignature == signature } } + fun findFunctionByMethod(method: Method): KmFunction? { + val signature = method.toSignature() + return functions.find { it.signature == signature } + } + companion object { fun createOrNull(clazz: Class<*>): JmClass? = clazz.toKmClass()?.let { JmClass(it) } } diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt index d5efcfde..bcae96b3 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt @@ -92,15 +92,11 @@ internal class KotlinPrimaryAnnotationIntrospector( private fun AnnotatedParameter.hasRequiredMarker(jmClass: JmClass): Boolean? { val paramDef = when (val member = member) { - is Constructor<*> -> jmClass.findKmConstructor(member) - ?.let { it.valueParameters[index] } - is Method -> { - val signature = member.toSignature() - jmClass.kmClass.functions.find { it.signature == signature } - ?.let { it.valueParameters[index] } - } + is Constructor<*> -> jmClass.findKmConstructor(member)?.valueParameters + + is Method -> jmClass.findFunctionByMethod(member)?.valueParameters else -> null - } ?: return null // Return null if function on Kotlin cannot be determined + }?.let { it[index] } ?: return null // Return null if function on Kotlin cannot be determined // non required if... return when { From 6573ec3b0d3dd153e332a3ad7c487d019850bd28 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:22:24 +0900 Subject: [PATCH 13/26] Fix to use JmClass --- .../kogera/deser/deserializers/KotlinDeserializers.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt index 12e4ef1a..f4309a4d 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt @@ -9,12 +9,12 @@ import com.fasterxml.jackson.databind.JsonDeserializer import com.fasterxml.jackson.databind.deser.Deserializers import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.fasterxml.jackson.databind.exc.InvalidDefinitionException +import io.github.projectmapk.jackson.module.kogera.JmClass import io.github.projectmapk.jackson.module.kogera.ReflectionCache import io.github.projectmapk.jackson.module.kogera.hasCreatorAnnotation import io.github.projectmapk.jackson.module.kogera.isUnboxableValueClass import io.github.projectmapk.jackson.module.kogera.toSignature import kotlinx.metadata.Flag -import kotlinx.metadata.KmClass import kotlinx.metadata.jvm.signature import java.lang.reflect.Method import java.lang.reflect.Modifier @@ -101,9 +101,9 @@ private fun invalidCreatorMessage(m: Method): String = "please fix it or use JsonDeserializer.\n" + "Detected: ${m.parameters.joinToString(prefix = "${m.name}(", separator = ", ", postfix = ")") { it.name }}" -private fun findValueCreator(type: JavaType, clazz: Class<*>, kmClass: KmClass): Method? { +private fun findValueCreator(type: JavaType, clazz: Class<*>, jmClass: JmClass): Method? { val primaryKmConstructorSignature = - kmClass.constructors.first { !Flag.Constructor.IS_SECONDARY(it.flags) }.signature + jmClass.constructors.first { !Flag.Constructor.IS_SECONDARY(it.flags) }.signature var primaryConstructor: Method? = null clazz.declaredMethods.forEach { method -> @@ -142,7 +142,7 @@ internal class KotlinDeserializers(private val cache: ReflectionCache) : Deseria rawClass == UShort::class.java -> UShortDeserializer rawClass == UInt::class.java -> UIntDeserializer rawClass == ULong::class.java -> ULongDeserializer - rawClass.isUnboxableValueClass() -> findValueCreator(type, rawClass, cache.getJmClass(rawClass)!!.kmClass) + rawClass.isUnboxableValueClass() -> findValueCreator(type, rawClass, cache.getJmClass(rawClass)!!) ?.let { ValueClassBoxDeserializer(it, rawClass) } else -> null } From ad50849f7be60c5245b7013a45f88379a8b7c503 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:23:31 +0900 Subject: [PATCH 14/26] Fix to use JmClass --- .../KotlinPrimaryAnnotationIntrospector.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt index bcae96b3..4996d19e 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt @@ -136,7 +136,7 @@ internal class KotlinPrimaryAnnotationIntrospector( ?: return null return JsonCreator.Mode.DEFAULT - .takeIf { ann.annotated.isPrimarilyConstructorOf(jmClass) && !hasCreator(declaringClass, jmClass.kmClass) } + .takeIf { ann.annotated.isPrimarilyConstructorOf(jmClass) && !hasCreator(declaringClass, jmClass) } } } @@ -154,8 +154,8 @@ private fun isPossibleSingleString( kotlinParams[0].let { it.name !in propertyNames && it.type.classifier.isString() } && javaFunction.parameters[0].annotations.none { it is JsonProperty } -private fun hasCreatorConstructor(clazz: Class<*>, kmClass: KmClass, propertyNames: Set): Boolean { - val kmConstructorMap = kmClass.constructors.associateBy { it.signature?.desc } +private fun hasCreatorConstructor(clazz: Class<*>, jmClass: JmClass, propertyNames: Set): Boolean { + val kmConstructorMap = jmClass.constructors.associateBy { it.signature?.desc } return clazz.constructors.any { constructor -> val kmConstructor = kmConstructorMap[constructor.toSignature().desc] ?: return@any false @@ -170,7 +170,7 @@ private fun hasCreatorConstructor(clazz: Class<*>, kmClass: KmClass, propertyNam private fun hasCreatorFunction(clazz: Class<*>): Boolean = clazz.declaredMethods .any { Modifier.isStatic(it.modifiers) && it.hasCreatorAnnotation() } -private fun hasCreator(clazz: Class<*>, kmClass: KmClass): Boolean { - val propertyNames = kmClass.properties.map { it.name }.toSet() - return hasCreatorConstructor(clazz, kmClass, propertyNames) || hasCreatorFunction(clazz) +private fun hasCreator(clazz: Class<*>, jmClass: JmClass): Boolean { + val propertyNames = jmClass.properties.map { it.name }.toSet() + return hasCreatorConstructor(clazz, jmClass, propertyNames) || hasCreatorFunction(clazz) } From b64c4db2702b744aa30cb0cec9c5de8092e0779d Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:25:24 +0900 Subject: [PATCH 15/26] Fix to use JmClass --- .../KotlinPrimaryAnnotationIntrospector.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt index 4996d19e..08b7646d 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt @@ -19,7 +19,6 @@ import io.github.projectmapk.jackson.module.kogera.isNullable import io.github.projectmapk.jackson.module.kogera.reconstructClass import io.github.projectmapk.jackson.module.kogera.toSignature import kotlinx.metadata.Flag -import kotlinx.metadata.KmClass import kotlinx.metadata.KmClassifier import kotlinx.metadata.KmProperty import kotlinx.metadata.KmValueParameter @@ -48,8 +47,8 @@ internal class KotlinPrimaryAnnotationIntrospector( return cache.getJmClass(m.member.declaringClass)?.let { when (m) { - is AnnotatedField -> m.hasRequiredMarker(it.kmClass) - is AnnotatedMethod -> m.getRequiredMarkerFromCorrespondingAccessor(it.kmClass) + is AnnotatedField -> m.hasRequiredMarker(it) + is AnnotatedMethod -> m.getRequiredMarkerFromCorrespondingAccessor(it) is AnnotatedParameter -> m.hasRequiredMarker(it) else -> null } @@ -63,13 +62,13 @@ internal class KotlinPrimaryAnnotationIntrospector( // The nullToEmpty option also affects serialization, // but deserialization is preferred because there is currently no way to distinguish between contexts. - private fun AnnotatedField.hasRequiredMarker(kmClass: KmClass): Boolean? { + private fun AnnotatedField.hasRequiredMarker(jmClass: JmClass): Boolean? { val member = annotated val fieldSignature = member.toSignature() // Direct access to `AnnotatedField` is only performed if there is no accessor (defined as JvmField), // so if an accessor is defined, it is ignored. - return kmClass.properties + return jmClass.properties .find { it.fieldSignature == fieldSignature } // Since a property that does not currently have a getter cannot be defined, // only a check for the existence of a getter is performed. @@ -80,12 +79,12 @@ internal class KotlinPrimaryAnnotationIntrospector( private fun KmProperty.isRequiredByNullability(): Boolean = !this.returnType.isNullable() - private fun AnnotatedMethod.getRequiredMarkerFromCorrespondingAccessor(kmClass: KmClass): Boolean? { + private fun AnnotatedMethod.getRequiredMarkerFromCorrespondingAccessor(jmClass: JmClass): Boolean? { // false if setter and nullToEmpty option is specified if (parameterCount == 1 && this.getParameter(0).type.hasDefaultEmptyValue()) return false val memberSignature = member.toSignature() - return kmClass.properties + return jmClass.properties .find { it.getterSignature == memberSignature || it.setterSignature == memberSignature } ?.isRequiredByNullability() } From d8c7f94b1a62d251f2fc5acdf1f0200358c70781 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:26:16 +0900 Subject: [PATCH 16/26] Impl sealedSubclasses --- .../io/github/projectmapk/jackson/module/kogera/JmClass.kt | 2 ++ .../KotlinPrimaryAnnotationIntrospector.kt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt index 34131aad..bc023b32 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt @@ -1,5 +1,6 @@ package io.github.projectmapk.jackson.module.kogera +import kotlinx.metadata.ClassName import kotlinx.metadata.KmClass import kotlinx.metadata.KmConstructor import kotlinx.metadata.KmFunction @@ -14,6 +15,7 @@ internal class JmClass(val kmClass: KmClass) { val constructors: List = kmClass.constructors val properties: List = kmClass.properties private val functions: List = kmClass.functions + val sealedSubclasses: List = kmClass.sealedSubclasses fun findKmConstructor(constructor: Constructor<*>): KmConstructor? { val descHead = constructor.parameterTypes.toDescBuilder() diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt index 08b7646d..3d6198db 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinPrimaryAnnotationIntrospector.kt @@ -116,7 +116,7 @@ internal class KotlinPrimaryAnnotationIntrospector( // The definition location was not changed from kotlin-module because // the result was the same whether it was defined in Primary or Fallback. override fun findSubtypes(a: Annotated): List? = cache.getJmClass(a.rawType)?.let { jmClass -> - jmClass.kmClass.sealedSubclasses.map { NamedType(it.reconstructClass()) }.ifEmpty { null } + jmClass.sealedSubclasses.map { NamedType(it.reconstructClass()) }.ifEmpty { null } } // Return Mode.DEFAULT if ann is a Primary Constructor and the condition is satisfied. From e6e65ece9d765211d7fa96ba54fbe7538c158d2e Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:38:40 +0900 Subject: [PATCH 17/26] Impl companion --- .../jackson/module/kogera/JmClass.kt | 28 +++++++++++++++++-- .../KotlinFallbackAnnotationIntrospector.kt | 9 ++---- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt index bc023b32..9cdb3a9c 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt @@ -8,14 +8,19 @@ import kotlinx.metadata.KmProperty import kotlinx.metadata.jvm.getterSignature import kotlinx.metadata.jvm.signature import java.lang.reflect.Constructor +import java.lang.reflect.Field import java.lang.reflect.Method // Jackson Metadata Class -internal class JmClass(val kmClass: KmClass) { +internal class JmClass( + private val clazz: Class<*>, + val kmClass: KmClass +) { val constructors: List = kmClass.constructors val properties: List = kmClass.properties private val functions: List = kmClass.functions val sealedSubclasses: List = kmClass.sealedSubclasses + val companion: CompanionObject? by lazy { CompanionObject.createOrNull(this) } fun findKmConstructor(constructor: Constructor<*>): KmConstructor? { val descHead = constructor.parameterTypes.toDescBuilder() @@ -47,7 +52,26 @@ internal class JmClass(val kmClass: KmClass) { return functions.find { it.signature == signature } } + internal class CompanionObject( + declaringClass: Class<*>, + companionObject: String + ) { + private val companionField: Field = declaringClass.getDeclaredField(companionObject) + private val companionObjectClass: Class<*> = companionField.type + private val kmClass: KmClass by lazy { companionObjectClass.toKmClass()!! } + + fun findFunctionByMethod(method: Method): KmFunction? { + val signature = method.toSignature() + return kmClass.functions.find { it.signature == signature } + } + + companion object { + fun createOrNull(jmClass: JmClass): CompanionObject? = jmClass.kmClass.companionObject + ?.let { CompanionObject(jmClass.clazz, it) } + } + } + companion object { - fun createOrNull(clazz: Class<*>): JmClass? = clazz.toKmClass()?.let { JmClass(it) } + fun createOrNull(clazz: Class<*>): JmClass? = clazz.toKmClass()?.let { JmClass(clazz, it) } } } diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt index e77ee914..80279000 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt @@ -53,13 +53,8 @@ internal class KotlinFallbackAnnotationIntrospector( is Method -> owner.takeIf { _ -> Modifier.isStatic(owner.modifiers) } ?.let { _ -> - val companion = cache.getJmClass(param.declaringClass)?.kmClass?.companionObject ?: return@let null - val companionKmClass = owner.declaringClass.getDeclaredField(companion) - .type - .let { cache.getJmClass(it) }!! - val signature = owner.toSignature() - - companionKmClass.kmClass.functions.find { it.signature == signature }?.valueParameters + val companion = cache.getJmClass(param.declaringClass)?.companion ?: return@let null + companion.findFunctionByMethod(owner)?.valueParameters } else -> null }?.let { it[param.index].name } From 120a7df08b5125c81d28e13adfbba3cc998b7fb5 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:45:51 +0900 Subject: [PATCH 18/26] Impl companion for MethodValueCreator --- .../jackson/module/kogera/JmClass.kt | 10 ++++- .../jackson/module/kogera/ReflectionCache.kt | 2 +- .../creator/MethodValueCreator.kt | 37 ++++--------------- 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt index 9cdb3a9c..3463dc14 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt @@ -57,8 +57,14 @@ internal class JmClass( companionObject: String ) { private val companionField: Field = declaringClass.getDeclaredField(companionObject) - private val companionObjectClass: Class<*> = companionField.type - private val kmClass: KmClass by lazy { companionObjectClass.toKmClass()!! } + val type: Class<*> = companionField.type + val isAccessible: Boolean = companionField.isAccessible + private val kmClass: KmClass by lazy { type.toKmClass()!! } + val instance: Any by lazy { + // To prevent the call from failing, save the initial value and then rewrite the flag. + if (!companionField.isAccessible) companionField.isAccessible = true + companionField.get(null) + } fun findFunctionByMethod(method: Method): KmFunction? { val signature = method.toSignature() diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt index f5f7e3db..bb711bcd 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt @@ -76,7 +76,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { creatorCache.get(creator) ?: run { getJmClass(creator.declaringClass)?.let { - val value = MethodValueCreator(creator, it.kmClass) + val value = MethodValueCreator(creator, it) creatorCache.putIfAbsent(creator, value) ?: value } } diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/value_instantiator/creator/MethodValueCreator.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/value_instantiator/creator/MethodValueCreator.kt index b67d6c0e..6fee6fce 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/value_instantiator/creator/MethodValueCreator.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/value_instantiator/creator/MethodValueCreator.kt @@ -1,60 +1,39 @@ package io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator +import io.github.projectmapk.jackson.module.kogera.JmClass import io.github.projectmapk.jackson.module.kogera.call import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.argument_bucket.ArgumentBucket import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.argument_bucket.BucketGenerator import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.calcMaskSize import io.github.projectmapk.jackson.module.kogera.getDeclaredMethodBy import io.github.projectmapk.jackson.module.kogera.hasVarargParam -import io.github.projectmapk.jackson.module.kogera.toKmClass -import io.github.projectmapk.jackson.module.kogera.toSignature -import kotlinx.metadata.KmClass import kotlinx.metadata.KmFunction -import kotlinx.metadata.jvm.signature -import java.lang.reflect.Field import java.lang.reflect.Method -internal class MethodValueCreator(private val method: Method, declaringKmClass: KmClass) : ValueCreator() { - // companion object is always present in the case of a factory function - private val companionField: Field - private val companionObjectClass: Class<*> - - override val isAccessible: Boolean +internal class MethodValueCreator(private val method: Method, declaringJmClass: JmClass) : ValueCreator() { + private val companion: JmClass.CompanionObject = declaringJmClass.companion!! + override val isAccessible: Boolean = method.isAccessible && companion.isAccessible override val callableName: String = method.name override val valueParameters: List override val bucketGenerator: BucketGenerator init { - val declaringClass = method.declaringClass - companionField = declaringClass.getDeclaredField(declaringKmClass.companionObject!!) - - // region: about accessibility - isAccessible = method.isAccessible && companionField.isAccessible - // To prevent the call from failing, save the initial value and then rewrite the flag. if (!method.isAccessible) method.isAccessible = true - if (!companionField.isAccessible) companionField.isAccessible = true - // endregion - - // region: read kotlin metadata information - companionObjectClass = companionField.type - val companionKmClass: KmClass = companionObjectClass.toKmClass()!! - val kmFunction: KmFunction = run { - val signature = method.toSignature() - companionKmClass.functions.first { signature == it.signature } - } + val kmFunction: KmFunction = companion.findFunctionByMethod(method)!! valueParameters = kmFunction.valueParameters.map { ValueParameter(it) } bucketGenerator = BucketGenerator(method.parameterTypes.asList(), kmFunction.valueParameters.hasVarargParam()) - // endregion } private val defaultCaller: (args: ArgumentBucket) -> Any? by lazy { val valueParameterSize = method.parameterTypes.size val maskSize = calcMaskSize(valueParameterSize) + val companionObjectClass = companion.type @Suppress("UNCHECKED_CAST") val defaultTypes = method.parameterTypes.let { parameterTypes -> + // companion object instance(1) + parameterSize + maskSize + marker(1) val temp = arrayOfNulls>(1 + valueParameterSize + maskSize + 1) temp[0] = companionObjectClass // companion object @@ -67,7 +46,7 @@ internal class MethodValueCreator(private val method: Method, declaringKmClas } as Array> val defaultMethod = companionObjectClass.getDeclaredMethodBy("${callableName}\$default", defaultTypes) - val companionObject = companionField.get(null) + val companionObject = companion.instance return@lazy { val defaultArgs = arrayOfNulls(defaultTypes.size) From 37f6ffc7435827b4bfa6db526faac088554439f0 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 14:59:56 +0900 Subject: [PATCH 19/26] Modified to cache only the minimum information in KmClass --- .../projectmapk/jackson/module/kogera/JmClass.kt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt index 3463dc14..5c38f4f8 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt @@ -14,13 +14,14 @@ import java.lang.reflect.Method // Jackson Metadata Class internal class JmClass( private val clazz: Class<*>, - val kmClass: KmClass + kmClass: KmClass ) { val constructors: List = kmClass.constructors val properties: List = kmClass.properties private val functions: List = kmClass.functions val sealedSubclasses: List = kmClass.sealedSubclasses - val companion: CompanionObject? by lazy { CompanionObject.createOrNull(this) } + private val companionPropName: String? = kmClass.companionObject + val companion: CompanionObject? by lazy { companionPropName?.let { CompanionObject(clazz, it) } } fun findKmConstructor(constructor: Constructor<*>): KmConstructor? { val descHead = constructor.parameterTypes.toDescBuilder() @@ -70,11 +71,6 @@ internal class JmClass( val signature = method.toSignature() return kmClass.functions.find { it.signature == signature } } - - companion object { - fun createOrNull(jmClass: JmClass): CompanionObject? = jmClass.kmClass.companionObject - ?.let { CompanionObject(jmClass.clazz, it) } - } } companion object { From 6eb354796a191cba854163d1c8d8dbd441fecab6 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 15:10:03 +0900 Subject: [PATCH 20/26] Modified to use ReflectionCache for parsing of KotlinBeanDeserializerModifier. --- .../jackson/module/kogera/JmClass.kt | 2 ++ .../jackson/module/kogera/KotlinModule.kt | 2 +- .../KotlinBeanDeserializerModifier.kt | 24 ++++++++++--------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt index 5c38f4f8..c85c85f7 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt @@ -1,6 +1,7 @@ package io.github.projectmapk.jackson.module.kogera import kotlinx.metadata.ClassName +import kotlinx.metadata.Flags import kotlinx.metadata.KmClass import kotlinx.metadata.KmConstructor import kotlinx.metadata.KmFunction @@ -16,6 +17,7 @@ internal class JmClass( private val clazz: Class<*>, kmClass: KmClass ) { + val flags: Flags = kmClass.flags val constructors: List = kmClass.constructors val properties: List = kmClass.properties private val functions: List = kmClass.functions diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt index dfa4d3ed..443624db 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt @@ -77,7 +77,7 @@ public class KotlinModule private constructor( ) if (singletonSupport) { - context.addBeanDeserializerModifier(KotlinBeanDeserializerModifier) + context.addBeanDeserializerModifier(KotlinBeanDeserializerModifier(cache)) } context.insertAnnotationIntrospector( diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/singleton_support/KotlinBeanDeserializerModifier.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/singleton_support/KotlinBeanDeserializerModifier.kt index 8ddb7b2b..9f7bff47 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/singleton_support/KotlinBeanDeserializerModifier.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/singleton_support/KotlinBeanDeserializerModifier.kt @@ -4,11 +4,22 @@ import com.fasterxml.jackson.databind.BeanDescription import com.fasterxml.jackson.databind.DeserializationConfig import com.fasterxml.jackson.databind.JsonDeserializer import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier -import io.github.projectmapk.jackson.module.kogera.toKmClass +import io.github.projectmapk.jackson.module.kogera.ReflectionCache import kotlinx.metadata.Flag // [module-kotlin#225]: keep Kotlin singletons as singletons -internal object KotlinBeanDeserializerModifier : BeanDeserializerModifier() { +internal class KotlinBeanDeserializerModifier(private val cache: ReflectionCache) : BeanDeserializerModifier() { + private fun objectSingletonInstance(beanClass: Class<*>): Any? = cache.getJmClass(beanClass)?.let { + val flags = it.flags + + // It is not assumed that the companion object is the target + if (Flag.Class.IS_OBJECT(flags) && !Flag.Class.IS_COMPANION_OBJECT(flags)) { + beanClass.getDeclaredField("INSTANCE").get(null) + } else { + null + } + } + override fun modifyDeserializer( config: DeserializationConfig, beanDesc: BeanDescription, @@ -21,12 +32,3 @@ internal object KotlinBeanDeserializerModifier : BeanDeserializerModifier() { ?: modifiedFromParent } } - -private fun objectSingletonInstance(beanClass: Class<*>): Any? = beanClass.toKmClass()?.let { - // It is not assumed that the companion object is the target - if (Flag.Class.IS_OBJECT(it.flags) && !Flag.Class.IS_COMPANION_OBJECT(it.flags)) { - beanClass.getDeclaredField("INSTANCE").get(null) - } else { - null - } -} From c8aa6899f1bb7de28c8328114a8a23afc16958a1 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 15:11:03 +0900 Subject: [PATCH 21/26] Make toKmClass function private --- .../projectmapk/jackson/module/kogera/InternalCommons.kt | 8 -------- .../github/projectmapk/jackson/module/kogera/JmClass.kt | 7 +++++++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt index cae722e4..30a1d338 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/InternalCommons.kt @@ -2,13 +2,11 @@ package io.github.projectmapk.jackson.module.kogera import com.fasterxml.jackson.annotation.JsonCreator import kotlinx.metadata.Flag -import kotlinx.metadata.KmClass import kotlinx.metadata.KmClassifier import kotlinx.metadata.KmType import kotlinx.metadata.KmValueParameter import kotlinx.metadata.jvm.JvmFieldSignature import kotlinx.metadata.jvm.JvmMethodSignature -import kotlinx.metadata.jvm.KotlinClassMetadata import java.lang.reflect.AnnotatedElement import java.lang.reflect.Constructor import java.lang.reflect.Field @@ -16,12 +14,6 @@ import java.lang.reflect.Method internal fun Class<*>.isUnboxableValueClass() = annotations.any { it is JvmInline } -internal fun Class<*>.toKmClass(): KmClass? = annotations - .filterIsInstance() - .firstOrNull() - ?.let { KotlinClassMetadata.read(it) as KotlinClassMetadata.Class } - ?.toKmClass() - private val primitiveClassToDesc by lazy { mapOf( Byte::class.javaPrimitiveType to 'B', diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt index c85c85f7..e15a2d7f 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt @@ -6,12 +6,19 @@ import kotlinx.metadata.KmClass import kotlinx.metadata.KmConstructor import kotlinx.metadata.KmFunction import kotlinx.metadata.KmProperty +import kotlinx.metadata.jvm.KotlinClassMetadata import kotlinx.metadata.jvm.getterSignature import kotlinx.metadata.jvm.signature import java.lang.reflect.Constructor import java.lang.reflect.Field import java.lang.reflect.Method +private fun Class<*>.toKmClass(): KmClass? = annotations + .filterIsInstance() + .firstOrNull() + ?.let { KotlinClassMetadata.read(it) as KotlinClassMetadata.Class } + ?.toKmClass() + // Jackson Metadata Class internal class JmClass( private val clazz: Class<*>, From da883b3e678bc47bb7064b45ed1a37346faf6de2 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 15:16:46 +0900 Subject: [PATCH 22/26] Remove unused import --- .../KotlinFallbackAnnotationIntrospector.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt index 80279000..6b219be7 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt @@ -24,7 +24,6 @@ import io.github.projectmapk.jackson.module.kogera.ser.SequenceToIteratorConvert import io.github.projectmapk.jackson.module.kogera.toSignature import kotlinx.metadata.jvm.fieldSignature import kotlinx.metadata.jvm.setterSignature -import kotlinx.metadata.jvm.signature import java.lang.reflect.Constructor import java.lang.reflect.Executable import java.lang.reflect.Method From f73da45fd937b6f03b83f927b7286f78a4b56d12 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 17:42:50 +0900 Subject: [PATCH 23/26] Annotations assigned to synthetic constructor parameters are not parsed by default. Since there is a workaround, the initialization performance was emphasized. --- .../jackson/module/kogera/KotlinFeature.kt | 20 ++++++++++++++++++- .../jackson/module/kogera/KotlinModule.kt | 12 ++++++++--- .../jackson/module/kogera/KotlinModuleTest.kt | 10 +++++----- .../deserializer/by_annotation/TempTest.kt | 11 ++++++++-- .../primitive/ByAnnotationTest.kt | 10 ++++++++-- 5 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinFeature.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinFeature.kt index 7cc18270..cd262481 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinFeature.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinFeature.kt @@ -55,7 +55,25 @@ public enum class KotlinFeature(internal val enabledByDefault: Boolean) { * may contain null values after deserialization. * Enabling it protects against this but has performance impact. */ - StrictNullChecks(enabledByDefault = false); + StrictNullChecks(enabledByDefault = false), + + /** + * This feature represents whether to include in Jackson's parsing the annotations given to the parameters of + * constructors that include value class as a parameter. + * + * Constructor with value class as a parameter on Kotlin are compiled into a public synthetic constructor + * and a private constructor. + * In this case, annotations are only given to synthetic constructors, + * so they are not normally included in Jackson's parsing and will not work. + * + * To work around this problem, annotations can be added by specifying a field or getter, + * or by enabling this feature. + * However, enabling this feature will affect initialization performance. + * Also note that enabling this feature does not enable annotations given to the constructor. + * + * @see KotlinClassIntrospector + */ + CopySyntheticConstructorParameterAnnotations(enabledByDefault = false); internal val bitSet: BitSet = (1 shl ordinal).toBitSet() diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt index 443624db..6e2c729d 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModule.kt @@ -2,6 +2,7 @@ package io.github.projectmapk.jackson.module.kogera import com.fasterxml.jackson.databind.MapperFeature import com.fasterxml.jackson.databind.module.SimpleModule +import io.github.projectmapk.jackson.module.kogera.KotlinFeature.CopySyntheticConstructorParameterAnnotations import io.github.projectmapk.jackson.module.kogera.KotlinFeature.NullIsSameAsDefault import io.github.projectmapk.jackson.module.kogera.KotlinFeature.NullToEmptyCollection import io.github.projectmapk.jackson.module.kogera.KotlinFeature.NullToEmptyMap @@ -40,7 +41,9 @@ public class KotlinModule private constructor( public val nullToEmptyMap: Boolean = NullToEmptyMap.enabledByDefault, public val nullIsSameAsDefault: Boolean = NullIsSameAsDefault.enabledByDefault, public val singletonSupport: Boolean = SingletonSupport.enabledByDefault, - public val strictNullChecks: Boolean = StrictNullChecks.enabledByDefault + public val strictNullChecks: Boolean = StrictNullChecks.enabledByDefault, + public val copySyntheticConstructorParameterAnnotations: Boolean = + CopySyntheticConstructorParameterAnnotations.enabledByDefault ) : SimpleModule(KotlinModule::class.java.name, kogeraVersion) { // kogeraVersion is generated by building. private constructor(builder: Builder) : this( builder.reflectionCacheSize, @@ -48,7 +51,8 @@ public class KotlinModule private constructor( builder.isEnabled(NullToEmptyMap), builder.isEnabled(NullIsSameAsDefault), builder.isEnabled(SingletonSupport), - builder.isEnabled(StrictNullChecks) + builder.isEnabled(StrictNullChecks), + builder.isEnabled(CopySyntheticConstructorParameterAnnotations) ) @Deprecated( @@ -85,7 +89,9 @@ public class KotlinModule private constructor( ) context.appendAnnotationIntrospector(KotlinFallbackAnnotationIntrospector(strictNullChecks, cache)) - context.setClassIntrospector(KotlinClassIntrospector) + if (copySyntheticConstructorParameterAnnotations) { + context.setClassIntrospector(KotlinClassIntrospector) + } context.addDeserializers(KotlinDeserializers(cache)) context.addKeyDeserializers(KotlinKeyDeserializers) diff --git a/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModuleTest.kt b/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModuleTest.kt index 5b6419f9..b99ed38b 100644 --- a/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModuleTest.kt +++ b/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/KotlinModuleTest.kt @@ -11,11 +11,10 @@ class KotlinModuleTest { fun jdkSerializabilityTest() { val module = KotlinModule.Builder().apply { withReflectionCacheSize(123) - enable(KotlinFeature.NullToEmptyCollection) - enable(KotlinFeature.NullToEmptyMap) - enable(KotlinFeature.NullIsSameAsDefault) - enable(KotlinFeature.SingletonSupport) - enable(KotlinFeature.StrictNullChecks) + + KotlinFeature.values().forEach { + enable(it) + } }.build() val serialized = jdkSerialize(module) @@ -30,6 +29,7 @@ class KotlinModuleTest { assertTrue(deserialized.nullIsSameAsDefault) assertTrue(deserialized.singletonSupport) assertTrue(deserialized.strictNullChecks) + assertTrue(deserialized.copySyntheticConstructorParameterAnnotations) } } } diff --git a/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_integration/deser/value_class/deserializer/by_annotation/TempTest.kt b/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_integration/deser/value_class/deserializer/by_annotation/TempTest.kt index ffe1b18e..0ff80323 100644 --- a/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_integration/deser/value_class/deserializer/by_annotation/TempTest.kt +++ b/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_integration/deser/value_class/deserializer/by_annotation/TempTest.kt @@ -1,8 +1,10 @@ package io.github.projectmapk.jackson.module.kogera._integration.deser.value_class.deserializer.by_annotation +import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import io.github.projectmapk.jackson.module.kogera.KotlinFeature +import io.github.projectmapk.jackson.module.kogera.KotlinModule import io.github.projectmapk.jackson.module.kogera._integration.deser.value_class.Primitive -import io.github.projectmapk.jackson.module.kogera.jacksonObjectMapper import io.github.projectmapk.jackson.module.kogera.readValue import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -15,7 +17,12 @@ class TempTest { @Test fun test() { - val result = jacksonObjectMapper().readValue("""{"value":1}""") + val result = KotlinModule.Builder() + .enable(KotlinFeature.CopySyntheticConstructorParameterAnnotations) + .build() + .let { ObjectMapper().registerModule(it) } + .readValue("""{"value":1}""") + assertEquals(Dst(Primitive(101)), result) } } diff --git a/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_integration/ser/value_class/serializer/by_annotation/primitive/ByAnnotationTest.kt b/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_integration/ser/value_class/serializer/by_annotation/primitive/ByAnnotationTest.kt index 319fec4d..03bbcccd 100644 --- a/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_integration/ser/value_class/serializer/by_annotation/primitive/ByAnnotationTest.kt +++ b/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/_integration/ser/value_class/serializer/by_annotation/primitive/ByAnnotationTest.kt @@ -1,15 +1,21 @@ package io.github.projectmapk.jackson.module.kogera._integration.ser.value_class.serializer.by_annotation.primitive +import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.annotation.JsonSerialize +import io.github.projectmapk.jackson.module.kogera.KotlinFeature +import io.github.projectmapk.jackson.module.kogera.KotlinModule import io.github.projectmapk.jackson.module.kogera._integration.ser.value_class.serializer.Primitive -import io.github.projectmapk.jackson.module.kogera.jacksonObjectMapper import io.github.projectmapk.jackson.module.kogera.testPrettyWriter import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test class ByAnnotationTest { companion object { - val writer = jacksonObjectMapper().testPrettyWriter() + val writer = KotlinModule.Builder() + .enable(KotlinFeature.CopySyntheticConstructorParameterAnnotations) + .build() + .let { ObjectMapper().registerModule(it) } + .testPrettyWriter() } data class NonNullSrc( From 0170576b66614f5a834bbd3b8bdc294131aeb99f Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 29 Jul 2023 18:10:55 +0900 Subject: [PATCH 24/26] Update AboutValueClass doc --- docs/AboutValueClassSupport.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/AboutValueClassSupport.md b/docs/AboutValueClassSupport.md index c67f69c7..ef2bc823 100644 --- a/docs/AboutValueClassSupport.md +++ b/docs/AboutValueClassSupport.md @@ -2,16 +2,18 @@ The `jackson-module-kogera` supports many use cases of `value class` (`inline cl This page summarizes the basic policy and points to note regarding the use of the `value class`. ## Note on the use of `value class` -`value class` is one of the distinctive features of `Kotlin`. -Many key use cases are not supported in `jackson-module-kotlin` because -the functions and properties associated with `value class` have a special representation on the `JVM`. +The `value class` is one of the `Kotlin` specific feature. +On the other hand, `jackson-module-kotlin` does not support deserialization of `value class` in particular. +Also, there are some features of serialization that do not work properly. -However, due to `Jackson` limitations, the same behavior as the normal class is not fully reproduced. +The reason for this is that `value class` is a special representation on the `JVM`. +Due to this difference, some cases cannot be handled by basic `Jackson` parsing, which assumes `Java`. Known issues related to `value class` can be found [here](https://github.com/ProjectMapK/jackson-module-kogera/issues?q=is%3Aissue+is%3Aopen+label%3A%22value+class%22). -Also, when using `Jackson`, there is a concern that the use of `value class` will rather degrade performance. -This is because `jackson-module-kogera` does a lot of reflection processing to support `value class` -(this concern will be confirmed in [kogera-benchmark](https://github.com/ProjectMapK/kogera-benchmark) in the future). +In addition, one of the features of the `value class` is improved performance, +but when using `Jackson` (not only `Jackson`, but also other libraries that use reflection), +the performance is rather reduced. +This can be confirmed from [kogera-benchmark](https://github.com/ProjectMapK/kogera-benchmark). For these reasons, I recommend careful consideration when using `value class`. From 5fb3d924c15d2e9189830f2d8fb368ffc27f5e92 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 31 Jul 2023 22:40:32 +0900 Subject: [PATCH 25/26] Optimization of StrictNullChecks option Because of the high cost of the comparison process compared to the original. --- .../KotlinFallbackAnnotationIntrospector.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt index 6b219be7..37e45e6f 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotation_introspector/KotlinFallbackAnnotationIntrospector.kt @@ -85,7 +85,7 @@ internal class KotlinFallbackAnnotationIntrospector( valueParameter.createValueClassUnboxConverterOrNull(rawType) ?: run { if (strictNullChecks) { - valueParameter.createStrictNullChecksConverterOrNull(a.type, rawType) + valueParameter.createStrictNullChecksConverterOrNull(a.type) } else { null } @@ -176,13 +176,13 @@ private fun ValueParameter.createValueClassUnboxConverterOrNull(rawType: Class<* // @see io.github.projectmapk.jackson.module.kogera._ported.test.StrictNullChecksTest#testListOfGenericWithNullValue private fun ValueParameter.isNullishTypeAt(index: Int) = arguments.getOrNull(index)?.isNullable ?: true -private fun ValueParameter.createStrictNullChecksConverterOrNull(type: JavaType, rawType: Class<*>): Converter<*, *>? { +private fun ValueParameter.createStrictNullChecksConverterOrNull(type: JavaType): Converter<*, *>? { return when { - Array::class.java.isAssignableFrom(rawType) && !this.isNullishTypeAt(0) -> + type.isArrayType && !this.isNullishTypeAt(0) -> CollectionValueStrictNullChecksConverter.ForArray(type, this.name) - Iterable::class.java.isAssignableFrom(rawType) && !this.isNullishTypeAt(0) -> + type.isCollectionLikeType && !this.isNullishTypeAt(0) -> CollectionValueStrictNullChecksConverter.ForIterable(type, this.name) - Map::class.java.isAssignableFrom(rawType) && !this.isNullishTypeAt(1) -> + type.isMapLikeType && !this.isNullishTypeAt(1) -> MapValueStrictNullChecksConverter(type, this.name) else -> null } From 1ee808b29403ee335061ebf4eaa393399a1e12bb Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Mon, 31 Jul 2023 22:50:43 +0900 Subject: [PATCH 26/26] Modified to not use isAssignableFrom Because all `Unsigned Integers` cannot be inherited --- .../module/kogera/ser/serializers/KotlinSerializers.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ser/serializers/KotlinSerializers.kt b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ser/serializers/KotlinSerializers.kt index b4eb6ced..7d91204c 100644 --- a/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ser/serializers/KotlinSerializers.kt +++ b/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ser/serializers/KotlinSerializers.kt @@ -85,10 +85,10 @@ internal class KotlinSerializers : Serializers.Base() { val rawClass = type.rawClass return when { - UByte::class.java.isAssignableFrom(rawClass) -> UByteSerializer - UShort::class.java.isAssignableFrom(rawClass) -> UShortSerializer - UInt::class.java.isAssignableFrom(rawClass) -> UIntSerializer - ULong::class.java.isAssignableFrom(rawClass) -> ULongSerializer + UByte::class.java == rawClass -> UByteSerializer + UShort::class.java == rawClass -> UShortSerializer + UInt::class.java == rawClass -> UIntSerializer + ULong::class.java == rawClass -> ULongSerializer // The priority of Unboxing needs to be lowered so as not to break the serialization of Unsigned Integers. rawClass.isUnboxableValueClass() -> ValueClassStaticJsonValueSerializer.createOrNull(rawClass) ?: ValueClassUnboxSerializer