Skip to content

Commit

Permalink
Reduction of information held in value-parameter-related
Browse files Browse the repository at this point in the history
  • Loading branch information
k163377 committed Jun 8, 2024
1 parent 49ab9bd commit 685c589
Show file tree
Hide file tree
Showing 12 changed files with 69 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ internal fun String.reconstructClass(): Class<*> {
return Class.forName(String(replaced))
}

internal fun KmType.reconstructClassOrNull(): Class<*>? = (classifier as? KmClassifier.Class)
?.let { kotlin.runCatching { it.name.reconstructClass() }.getOrNull() }
internal fun KmType.reconstructClassOrNull(): Class<*>? = (classifier as? KmClassifier.Class)?.reconstructClassOrNull()
internal fun KmClassifier.Class.reconstructClassOrNull(): Class<*>? =
runCatching { name.reconstructClass() }.getOrNull()

internal fun AnnotatedElement.hasCreatorAnnotation(): Boolean = getAnnotation(JSON_CREATOR_CLASS)
?.let { it.mode != JsonCreator.Mode.DISABLED }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ import io.github.projectmapk.jackson.module.kogera.JSON_K_UNBOX_CLASS
import io.github.projectmapk.jackson.module.kogera.KOTLIN_DURATION_CLASS
import io.github.projectmapk.jackson.module.kogera.ReflectionCache
import io.github.projectmapk.jackson.module.kogera.isUnboxableValueClass
import io.github.projectmapk.jackson.module.kogera.reconstructClassOrNull
import io.github.projectmapk.jackson.module.kogera.jmClass.JmValueParameter
import io.github.projectmapk.jackson.module.kogera.ser.KotlinDurationValueToJavaDurationConverter
import io.github.projectmapk.jackson.module.kogera.ser.KotlinToJavaDurationConverter
import io.github.projectmapk.jackson.module.kogera.ser.SequenceToIteratorConverter
import kotlinx.metadata.KmTypeProjection
import kotlinx.metadata.KmValueParameter
import kotlinx.metadata.isNullable
import java.lang.reflect.Constructor
import java.lang.reflect.Method
Expand All @@ -35,7 +34,7 @@ internal class KotlinFallbackAnnotationIntrospector(
private val useJavaDurationConversion: Boolean,
private val cache: ReflectionCache
) : NopAnnotationIntrospector() {
private fun findKotlinParameter(param: AnnotatedParameter): KmValueParameter? =
private fun findKotlinParameter(param: AnnotatedParameter): JmValueParameter? =
when (val owner = param.owner.member) {
is Constructor<*> -> cache.getJmClass(param.declaringClass)?.findJmConstructor(owner)?.valueParameters
is Method -> if (Modifier.isStatic(owner.modifiers)) {
Expand All @@ -48,7 +47,7 @@ internal class KotlinFallbackAnnotationIntrospector(
else -> null
}?.let { it[param.index] }

private fun findKotlinParameter(param: Annotated): KmValueParameter? =
private fun findKotlinParameter(param: Annotated): JmValueParameter? =
(param as? AnnotatedParameter)?.let { findKotlinParameter(it) }

// since 2.4
Expand All @@ -65,7 +64,7 @@ internal class KotlinFallbackAnnotationIntrospector(
override fun refineDeserializationType(config: MapperConfig<*>, a: Annotated, baseType: JavaType): JavaType =
findKotlinParameter(a)?.let { param ->
val rawType = a.rawType
param.type.reconstructClassOrNull()
param.reconstructedClassOrNull
?.takeIf { it.isUnboxableValueClass() && it != rawType }
?.let { config.constructType(it) }
} ?: baseType
Expand Down Expand Up @@ -123,11 +122,11 @@ internal class KotlinFallbackAnnotationIntrospector(
?: super.findSetterInfo(ann)
}

private fun KmValueParameter.isNullishTypeAt(index: Int): Boolean = type.arguments.getOrNull(index)?.let {
private fun JmValueParameter.isNullishTypeAt(index: Int): Boolean = arguments.getOrNull(index)?.let {
// If it is not a StarProjection, type is not null
it === KmTypeProjection.STAR || it.type!!.isNullable
} ?: true // If a type argument cannot be taken, treat it as nullable to avoid unexpected failure.

private fun KmValueParameter.requireStrictNullCheck(type: JavaType): Boolean =
private fun JmValueParameter.requireStrictNullCheck(type: JavaType): Boolean =
((type.isArrayType || type.isCollectionLikeType) && !this.isNullishTypeAt(0)) ||
(type.isMapLikeType && !this.isNullishTypeAt(1))
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ import io.github.projectmapk.jackson.module.kogera.ReflectionCache
import io.github.projectmapk.jackson.module.kogera.hasCreatorAnnotation
import io.github.projectmapk.jackson.module.kogera.jmClass.JmClass
import io.github.projectmapk.jackson.module.kogera.jmClass.JmProperty
import io.github.projectmapk.jackson.module.kogera.jmClass.JmValueParameter
import io.github.projectmapk.jackson.module.kogera.reconstructClass
import io.github.projectmapk.jackson.module.kogera.toSignature
import kotlinx.metadata.KmClassifier
import kotlinx.metadata.KmValueParameter
import kotlinx.metadata.declaresDefaultValue
import kotlinx.metadata.isNullable
import java.lang.reflect.Constructor
import java.lang.reflect.Executable
Expand Down Expand Up @@ -97,11 +96,11 @@ internal class KotlinPrimaryAnnotationIntrospector(
// non required if...
return when {
// Argument definition is nullable
paramDef.type.isNullable -> false
paramDef.isNullable -> false
// Default argument are defined
paramDef.declaresDefaultValue -> false
paramDef.isOptional -> false
// vararg is treated as an empty array because undefined input is allowed
paramDef.varargElementType != null -> false
paramDef.isVararg -> false
// The conversion in case of null is defined.
type.hasDefaultEmptyValue() -> false
else -> true
Expand Down Expand Up @@ -144,11 +143,11 @@ private fun Constructor<*>.isPrimarilyConstructorOf(jmClass: JmClass): Boolean =
private fun KmClassifier.isString(): Boolean = this is KmClassifier.Class && this.name == "kotlin/String"

private fun isPossibleSingleString(
kotlinParams: List<KmValueParameter>,
kotlinParams: List<JmValueParameter>,
javaFunction: Executable,
propertyNames: Set<String>
): Boolean = kotlinParams.size == 1 &&
kotlinParams[0].let { it.name !in propertyNames && it.type.classifier.isString() } &&
kotlinParams[0].let { it.name !in propertyNames && it.isString } &&
javaFunction.parameters[0].annotations.none { it is JsonProperty }

private fun hasCreatorConstructor(clazz: Class<*>, jmClass: JmClass, propertyNames: Set<String>): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.argu

import io.github.projectmapk.jackson.module.kogera.ValueClassUnboxConverter
import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.calcMaskSize
import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.creator.ValueParameter
import io.github.projectmapk.jackson.module.kogera.jmClass.JmValueParameter
import java.lang.reflect.Array as ReflectArray

private fun defaultPrimitiveValue(type: Class<*>): Any = when (type) {
Expand Down Expand Up @@ -47,7 +47,7 @@ private fun IntArray.update(index: Int, operation: MaskOperation) {
// @see https://github.com/JetBrains/kotlin/blob/4c925d05883a8073e6732bca95bf575beb031a59/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt#L114
internal class BucketGenerator(
parameterTypes: List<Class<*>>,
valueParameters: List<ValueParameter>,
valueParameters: List<JmValueParameter>,
private val converters: List<ValueClassUnboxConverter<Any>?>
) {
private val valueParameterSize: Int = parameterTypes.size
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.argum
import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.calcMaskSize
import io.github.projectmapk.jackson.module.kogera.getDeclaredConstructorBy
import io.github.projectmapk.jackson.module.kogera.jmClass.JmClass
import io.github.projectmapk.jackson.module.kogera.jmClass.JmValueParameter
import java.lang.reflect.Constructor

internal class ConstructorValueCreator<T : Any>(
Expand All @@ -19,21 +20,19 @@ internal class ConstructorValueCreator<T : Any>(

override val isAccessible: Boolean = constructor.isAccessible
override val callableName: String = constructor.name
override val valueParameters: List<ValueParameter>
override val valueParameters: List<JmValueParameter>
override val bucketGenerator: BucketGenerator

init {
// To prevent the call from failing, save the initial value and then rewrite the flag.
if (!isAccessible) constructor.isAccessible = true

val constructorParameters = declaringJmClass.findJmConstructor(constructor)!!.valueParameters

valueParameters = constructorParameters.map { ValueParameter(it) }
valueParameters = declaringJmClass.findJmConstructor(constructor)!!.valueParameters
val rawTypes = constructor.parameterTypes.asList()
bucketGenerator = BucketGenerator(
rawTypes,
valueParameters,
constructorParameters.mapToConverters(rawTypes, cache)
valueParameters.mapToConverters(rawTypes, cache)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.argum
import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.calcMaskSize
import io.github.projectmapk.jackson.module.kogera.getDeclaredMethodBy
import io.github.projectmapk.jackson.module.kogera.jmClass.JmClass
import io.github.projectmapk.jackson.module.kogera.jmClass.JmValueParameter
import java.lang.reflect.Method

internal class MethodValueCreator<T>(
Expand All @@ -18,22 +19,21 @@ internal class MethodValueCreator<T>(
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<ValueParameter>
override val valueParameters: List<JmValueParameter>
override val bucketGenerator: BucketGenerator

init {
// To prevent the call from failing, save the initial value and then rewrite the flag.
if (!method.isAccessible) method.isAccessible = true

val function = companion.findFunctionByMethod(method)!!
val kmParameters = function.valueParameters

valueParameters = kmParameters.map { ValueParameter(it) }
valueParameters = function.valueParameters
val rawTypes = method.parameterTypes.asList()
bucketGenerator = BucketGenerator(
rawTypes,
valueParameters,
kmParameters.mapToConverters(rawTypes, cache)
valueParameters.mapToConverters(rawTypes, cache)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import io.github.projectmapk.jackson.module.kogera.ValueClassUnboxConverter
import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.argumentBucket.ArgumentBucket
import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.argumentBucket.BucketGenerator
import io.github.projectmapk.jackson.module.kogera.isUnboxableValueClass
import io.github.projectmapk.jackson.module.kogera.reconstructClassOrNull
import kotlinx.metadata.KmValueParameter
import io.github.projectmapk.jackson.module.kogera.jmClass.JmValueParameter

/**
* A class that abstracts the creation of instances by calling KFunction.
Expand All @@ -28,7 +27,7 @@ internal sealed class ValueCreator<T> {
/**
* ValueParameters of the KFunction to be called.
*/
abstract val valueParameters: List<ValueParameter>
abstract val valueParameters: List<JmValueParameter>

protected abstract val bucketGenerator: BucketGenerator

Expand Down Expand Up @@ -61,12 +60,12 @@ internal sealed class ValueCreator<T> {
}

@Suppress("UNCHECKED_CAST")
internal fun List<KmValueParameter>.mapToConverters(
internal fun List<JmValueParameter>.mapToConverters(
rawTypes: List<Class<*>>,
cache: ReflectionCache
): List<ValueClassUnboxConverter<Any>?> =
mapIndexed { i, param ->
param.type.reconstructClassOrNull()
param.reconstructedClassOrNull
?.takeIf { it.isUnboxableValueClass() && rawTypes[i] != it }
?.let { cache.getValueClassUnboxConverter(it) }
} as List<ValueClassUnboxConverter<Any>?> // Cast to cheat generics

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package io.github.projectmapk.jackson.module.kogera.jmClass

import kotlinx.metadata.KmConstructor
import kotlinx.metadata.KmValueParameter
import kotlinx.metadata.isSecondary
import kotlinx.metadata.jvm.JvmMethodSignature
import kotlinx.metadata.jvm.signature

internal data class JmConstructor(
val isSecondary: Boolean,
val signature: JvmMethodSignature?,
val valueParameters: List<KmValueParameter>
val valueParameters: List<JmValueParameter>
) {
constructor(constructor: KmConstructor) : this(
isSecondary = constructor.isSecondary,
signature = constructor.signature,
valueParameters = constructor.valueParameters
valueParameters = constructor.valueParameters.map { JmValueParameter(it) }
)
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package io.github.projectmapk.jackson.module.kogera.jmClass

import kotlinx.metadata.KmFunction
import kotlinx.metadata.KmValueParameter
import kotlinx.metadata.jvm.JvmMethodSignature
import kotlinx.metadata.jvm.signature

internal class JmFunction(
val signature: JvmMethodSignature?,
val valueParameters: List<KmValueParameter>
val valueParameters: List<JmValueParameter>
) {
constructor(function: KmFunction) : this(function.signature, function.valueParameters)
constructor(function: KmFunction) : this(function.signature, function.valueParameters.map { JmValueParameter(it) })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.github.projectmapk.jackson.module.kogera.jmClass

import io.github.projectmapk.jackson.module.kogera.reconstructClassOrNull
import kotlinx.metadata.KmClassifier
import kotlinx.metadata.KmType
import kotlinx.metadata.KmTypeProjection
import kotlinx.metadata.KmValueParameter
import kotlinx.metadata.declaresDefaultValue
import kotlinx.metadata.isNullable

internal class JmValueParameter(
val name: String,
val isOptional: Boolean,
val isVararg: Boolean,
type: KmType,
classifier: KmClassifier.Class?
) {
constructor(valueParameter: KmValueParameter) : this(
valueParameter.name,
isOptional = valueParameter.declaresDefaultValue,
isVararg = valueParameter.varargElementType != null,
valueParameter.type,
valueParameter.type.classifier as? KmClassifier.Class
)

val isNullable: Boolean = type.isNullable
val isGenericType: Boolean = type.classifier is KmClassifier.TypeParameter
val arguments: List<KmTypeProjection> = type.arguments

val isString: Boolean = classifier?.name == "kotlin/String"
val reconstructedClassOrNull: Class<*>? by lazy {
classifier?.reconstructClassOrNull()
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.argumentBucket

import io.github.projectmapk.jackson.module.kogera.ValueClassUnboxConverter
import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.creator.ValueParameter
import io.github.projectmapk.jackson.module.kogera.jmClass.JmValueParameter
import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Assertions.assertEquals
Expand All @@ -15,7 +15,7 @@ private class ArgumentBucketTest {
every { this@mockk[any()] } returns null
}

fun mockValueParameter(mockVararg: Boolean = false, mockOptional: Boolean = false) = mockk<ValueParameter> {
fun mockValueParameter(mockVararg: Boolean = false, mockOptional: Boolean = false) = mockk<JmValueParameter> {
every { isVararg } returns mockVararg
every { isOptional } returns mockOptional
}
Expand Down

0 comments on commit 685c589

Please sign in to comment.