Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed KotrinFallbackAnnotationIntrospector processing to be independent of ValueCreator/ValueParameter #126

Merged
merged 4 commits into from
Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ 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
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.isNullable
import io.github.projectmapk.jackson.module.kogera.isUnboxableValueClass
import io.github.projectmapk.jackson.module.kogera.reconstructClassOrNull
import io.github.projectmapk.jackson.module.kogera.ser.SequenceToIteratorConverter
import io.github.projectmapk.jackson.module.kogera.toSignature
import kotlinx.metadata.KmTypeProjection
import kotlinx.metadata.KmValueParameter
import kotlinx.metadata.jvm.fieldSignature
import kotlinx.metadata.jvm.setterSignature
import java.lang.reflect.Constructor
import java.lang.reflect.Executable
import java.lang.reflect.Method
import java.lang.reflect.Modifier

Expand All @@ -36,28 +36,29 @@ internal class KotlinFallbackAnnotationIntrospector(
private val strictNullChecks: Boolean,
private val cache: ReflectionCache
) : NopAnnotationIntrospector() {
private fun findKotlinParameter(param: AnnotatedParameter): KmValueParameter? =
when (val owner = param.owner.member) {
is Constructor<*> -> cache.getJmClass(param.declaringClass)?.findKmConstructor(owner)?.valueParameters
is Method ->
owner.takeIf { _ -> Modifier.isStatic(owner.modifiers) }
?.let { _ ->
val companion = cache.getJmClass(param.declaringClass)?.companion ?: return@let null
companion.findFunctionByMethod(owner)?.valueParameters
}
else -> null
}?.let { it[param.index] }

// 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
} else {
null
}
is AnnotatedParameter -> findKotlinParameterName(member)
is AnnotatedParameter -> findKotlinParameter(member)?.name
else -> null
}

private fun findKotlinParameterName(param: AnnotatedParameter): String? = when (val owner = param.owner.member) {
is Constructor<*> -> cache.getJmClass(param.declaringClass)?.findKmConstructor(owner)?.valueParameters
is Method ->
owner.takeIf { _ -> Modifier.isStatic(owner.modifiers) }
?.let { _ ->
val companion = cache.getJmClass(param.declaringClass)?.companion ?: return@let null
companion.findFunctionByMethod(owner)?.valueParameters
}
else -> null
}?.let { it[param.index].name }

// 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.getJmClass(ann.declaringClass)?.let { jmClass ->
Expand All @@ -75,12 +76,9 @@ internal class KotlinFallbackAnnotationIntrospector(
}
}

private fun getValueParameter(a: AnnotatedParameter): ValueParameter? =
cache.valueCreatorFromJava(a.owner.annotated as Executable)?.let { it.valueParameters[a.index] }

// returns Converter when the argument on Java is an unboxed value class
override fun findDeserializationConverter(a: Annotated): Any? = (a as? AnnotatedParameter)?.let { param ->
getValueParameter(param)?.let { valueParameter ->
findKotlinParameter(param)?.let { valueParameter ->
val rawType = a.rawType

valueParameter.createValueClassUnboxConverterOrNull(rawType) ?: run {
Expand Down Expand Up @@ -166,17 +164,18 @@ internal object ClosedRangeHelpers {
}
}

private fun ValueParameter.createValueClassUnboxConverterOrNull(rawType: Class<*>): ValueClassUnboxConverter<*>? {
private fun KmValueParameter.createValueClassUnboxConverterOrNull(rawType: Class<*>): ValueClassUnboxConverter<*>? {
return type.reconstructClassOrNull()
?.takeIf { it.isUnboxableValueClass() && it != rawType }
?.let { ValueClassUnboxConverter(it) }
}

// If the collection type argument cannot be obtained, treat it as nullable
// @see io.github.projectmapk.jackson.module.kogera._ported.test.StrictNullChecksTest#testListOfGenericWithNullValue
private fun ValueParameter.isNullishTypeAt(index: Int) = arguments.getOrNull(index)?.isNullable ?: true
private fun KmValueParameter.isNullishTypeAt(index: Int): Boolean = type.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 ValueParameter.createStrictNullChecksConverterOrNull(type: JavaType): Converter<*, *>? {
private fun KmValueParameter.createStrictNullChecksConverterOrNull(type: JavaType): Converter<*, *>? {
return when {
type.isArrayType && !this.isNullishTypeAt(0) ->
CollectionValueStrictNullChecksConverter.ForArray(type, this.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,16 @@ import io.github.projectmapk.jackson.module.kogera.isNullable
import kotlinx.metadata.Flag
import kotlinx.metadata.KmClassifier
import kotlinx.metadata.KmType
import kotlinx.metadata.KmTypeProjection
import kotlinx.metadata.KmValueParameter

internal class ValueParameter(private val param: KmValueParameter) {
internal sealed interface Argument {
val isNullable: Boolean
val name: String?

object Star : Argument {
override val isNullable: Boolean = true
override val name: String? = null
}

class ArgumentImpl(type: KmType) : Argument {
override val isNullable: Boolean = type.isNullable()

// TODO: Formatting because it is a minimal display about the error content
override val name: String = type.classifier.toString()
}
}

internal class ValueParameter(param: KmValueParameter) {
val name: String = param.name
val type: KmType = param.type
val isOptional: Boolean = Flag.ValueParameter.DECLARES_DEFAULT_VALUE(param.flags)
val isPrimitive: Boolean = Flag.IS_PRIVATE(param.type.flags)
val isNullable: Boolean = type.isNullable()
val isGenericType: Boolean = param.type.classifier is KmClassifier.TypeParameter

val arguments: List<Argument> by lazy {
param.type.arguments.map {
if (it === KmTypeProjection.STAR) {
Argument.Star
} else {
// If it is not a StarProjection, type is not null
Argument.ArgumentImpl(it.type!!)
}
}
}

// TODO: Formatting into a form that is easy to understand as an error message with reference to KParameter
override fun toString() = "parameter name: ${param.name} parameter type: ${param.type.classifier}"
override fun toString() = "parameter name: $name parameter type: ${type.classifier}"
}