Merge branch '6.1.x'

This commit is contained in:
Sébastien Deleuze 2024-03-03 22:26:37 +01:00
commit 219004ef13
6 changed files with 86 additions and 20 deletions

View File

@ -115,18 +115,20 @@ public abstract class CoroutinesUtils {
switch (parameter.getKind()) { switch (parameter.getKind()) {
case INSTANCE -> argMap.put(parameter, target); case INSTANCE -> argMap.put(parameter, target);
case VALUE, EXTENSION_RECEIVER -> { case VALUE, EXTENSION_RECEIVER -> {
if (!parameter.isOptional() || args[index] != null) { Object arg = args[index];
if (!(parameter.isOptional() && arg == null)) {
if (parameter.getType().getClassifier() instanceof KClass<?> kClass) { if (parameter.getType().getClassifier() instanceof KClass<?> kClass) {
Class<?> javaClass = JvmClassMappingKt.getJavaClass(kClass); Class<?> javaClass = JvmClassMappingKt.getJavaClass(kClass);
if (KotlinDetector.isInlineClass(javaClass)) { if (KotlinDetector.isInlineClass(javaClass)
argMap.put(parameter, KClasses.getPrimaryConstructor(kClass).call(args[index])); && !(parameter.getType().isMarkedNullable() && arg == null)) {
argMap.put(parameter, KClasses.getPrimaryConstructor(kClass).call(arg));
} }
else { else {
argMap.put(parameter, args[index]); argMap.put(parameter, arg);
} }
} }
else { else {
argMap.put(parameter, args[index]); argMap.put(parameter, arg);
} }
} }
index++; index++;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -82,6 +82,15 @@ class CoroutinesUtilsTests {
.verify() .verify()
} }
@Test
fun invokeSuspendingFunctionWithNullableParameter() {
val method = CoroutinesUtilsTests::class.java.getDeclaredMethod("suspendingFunctionWithNullable", String::class.java, Continuation::class.java)
val mono = CoroutinesUtils.invokeSuspendingFunction(method, this, null, null) as Mono
runBlocking {
Assertions.assertThat(mono.awaitSingleOrNull()).isNull()
}
}
@Test @Test
fun invokeNonSuspendingFunction() { fun invokeNonSuspendingFunction() {
val method = CoroutinesUtilsTests::class.java.getDeclaredMethod("nonSuspendingFunction", String::class.java) val method = CoroutinesUtilsTests::class.java.getDeclaredMethod("nonSuspendingFunction", String::class.java)
@ -165,6 +174,15 @@ class CoroutinesUtilsTests {
} }
} }
@Test
fun invokeSuspendingFunctionWithNullableValueClassParameter() {
val method = CoroutinesUtilsTests::class.java.declaredMethods.first { it.name.startsWith("suspendingFunctionWithNullableValueClass") }
val mono = CoroutinesUtils.invokeSuspendingFunction(method, this, null, null) as Mono
runBlocking {
Assertions.assertThat(mono.awaitSingleOrNull()).isNull()
}
}
@Test @Test
fun invokeSuspendingFunctionWithExtension() { fun invokeSuspendingFunctionWithExtension() {
val method = CoroutinesUtilsTests::class.java.getDeclaredMethod("suspendingFunctionWithExtension", val method = CoroutinesUtilsTests::class.java.getDeclaredMethod("suspendingFunctionWithExtension",
@ -190,6 +208,11 @@ class CoroutinesUtilsTests {
return value return value
} }
suspend fun suspendingFunctionWithNullable(value: String?): String? {
delay(1)
return value
}
suspend fun suspendingFunctionWithFlow(): Flow<String> { suspend fun suspendingFunctionWithFlow(): Flow<String> {
delay(1) delay(1)
return flowOf("foo", "bar") return flowOf("foo", "bar")
@ -222,6 +245,11 @@ class CoroutinesUtilsTests {
return value.value return value.value
} }
suspend fun suspendingFunctionWithNullableValueClass(value: ValueClass?): String? {
delay(1)
return value?.value
}
suspend fun CustomException.suspendingFunctionWithExtension(): String { suspend fun CustomException.suspendingFunctionWithExtension(): String {
delay(1) delay(1)
return "${this.message}" return "${this.message}"

View File

@ -313,18 +313,20 @@ public class InvocableHandlerMethod extends HandlerMethod {
switch (parameter.getKind()) { switch (parameter.getKind()) {
case INSTANCE -> argMap.put(parameter, target); case INSTANCE -> argMap.put(parameter, target);
case VALUE, EXTENSION_RECEIVER -> { case VALUE, EXTENSION_RECEIVER -> {
if (!parameter.isOptional() || args[index] != null) { Object arg = args[index];
if (!(parameter.isOptional() && arg == null)) {
if (parameter.getType().getClassifier() instanceof KClass<?> kClass) { if (parameter.getType().getClassifier() instanceof KClass<?> kClass) {
Class<?> javaClass = JvmClassMappingKt.getJavaClass(kClass); Class<?> javaClass = JvmClassMappingKt.getJavaClass(kClass);
if (KotlinDetector.isInlineClass(javaClass)) { if (KotlinDetector.isInlineClass(javaClass)
argMap.put(parameter, KClasses.getPrimaryConstructor(kClass).call(args[index])); && !(parameter.getType().isMarkedNullable() && arg == null)) {
argMap.put(parameter, KClasses.getPrimaryConstructor(kClass).call(arg));
} }
else { else {
argMap.put(parameter, args[index]); argMap.put(parameter, arg);
} }
} }
else { else {
argMap.put(parameter, args[index]); argMap.put(parameter, arg);
} }
} }
index++; index++;

View File

@ -105,6 +105,13 @@ class InvocableHandlerMethodKotlinTests {
Assertions.assertThatIllegalArgumentException().isThrownBy { invocable.invokeForRequest(request, null) } Assertions.assertThatIllegalArgumentException().isThrownBy { invocable.invokeForRequest(request, null) }
} }
@Test
fun valueClassWithNullable() {
composite.addResolver(StubArgumentResolver(LongValueClass::class.java, null))
val value = getInvocable(ValueClassHandler::class.java, LongValueClass::class.java).invokeForRequest(request, null)
Assertions.assertThat(value).isNull()
}
@Test @Test
fun propertyAccessor() { fun propertyAccessor() {
val value = getInvocable(PropertyAccessorHandler::class.java).invokeForRequest(request, null) val value = getInvocable(PropertyAccessorHandler::class.java).invokeForRequest(request, null)
@ -173,6 +180,9 @@ class InvocableHandlerMethodKotlinTests {
fun valueClassWithInit(valueClass: ValueClassWithInit) = fun valueClassWithInit(valueClass: ValueClassWithInit) =
valueClass valueClass
fun valueClassWithNullable(limit: LongValueClass?) =
limit?.value
} }
private class PropertyAccessorHandler { private class PropertyAccessorHandler {

View File

@ -324,18 +324,20 @@ public class InvocableHandlerMethod extends HandlerMethod {
switch (parameter.getKind()) { switch (parameter.getKind()) {
case INSTANCE -> argMap.put(parameter, target); case INSTANCE -> argMap.put(parameter, target);
case VALUE, EXTENSION_RECEIVER -> { case VALUE, EXTENSION_RECEIVER -> {
if (!parameter.isOptional() || args[index] != null) { Object arg = args[index];
if (!(parameter.isOptional() && arg == null)) {
if (parameter.getType().getClassifier() instanceof KClass<?> kClass) { if (parameter.getType().getClassifier() instanceof KClass<?> kClass) {
Class<?> javaClass = JvmClassMappingKt.getJavaClass(kClass); Class<?> javaClass = JvmClassMappingKt.getJavaClass(kClass);
if (KotlinDetector.isInlineClass(javaClass)) { if (KotlinDetector.isInlineClass(javaClass)
argMap.put(parameter, KClasses.getPrimaryConstructor(kClass).call(args[index])); && !(parameter.getType().isMarkedNullable() && arg == null)) {
argMap.put(parameter, KClasses.getPrimaryConstructor(kClass).call(arg));
} }
else { else {
argMap.put(parameter, args[index]); argMap.put(parameter, arg);
} }
} }
else { else {
argMap.put(parameter, args[index]); argMap.put(parameter, arg);
} }
} }
index++; index++;

View File

@ -19,7 +19,6 @@ package org.springframework.web.reactive.result
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.springframework.core.MethodParameter import org.springframework.core.MethodParameter
@ -178,11 +177,19 @@ class InvocableHandlerMethodKotlinTests {
@Test @Test
fun nullReturnValue() { fun nullReturnValue() {
val method = NullResultController::nullable.javaMethod!! val method = NullResultController::nullableReturnValue.javaMethod!!
val result = invoke(NullResultController(), method) val result = invoke(NullResultController(), method)
assertHandlerResultValue(result, null) assertHandlerResultValue(result, null)
} }
@Test
fun nullParameter() {
this.resolvers.add(stubResolver(null, String::class.java))
val method = NullResultController::nullableParameter.javaMethod!!
val result = invoke(NullResultController(), method, null)
assertHandlerResultValue(result, null)
}
@Test @Test
fun valueClass() { fun valueClass() {
this.resolvers.add(stubResolver(1L, Long::class.java)) this.resolvers.add(stubResolver(1L, Long::class.java))
@ -192,7 +199,7 @@ class InvocableHandlerMethodKotlinTests {
} }
@Test @Test
fun valueClassDefaultValue() { fun valueClassWithDefaultValue() {
this.resolvers.add(stubResolver(null, Double::class.java)) this.resolvers.add(stubResolver(null, Double::class.java))
val method = ValueClassController::valueClassWithDefault.javaMethod!! val method = ValueClassController::valueClassWithDefault.javaMethod!!
val result = invoke(ValueClassController(), method) val result = invoke(ValueClassController(), method)
@ -207,6 +214,14 @@ class InvocableHandlerMethodKotlinTests {
assertExceptionThrown(result, IllegalArgumentException::class) assertExceptionThrown(result, IllegalArgumentException::class)
} }
@Test
fun valueClassWithNullable() {
this.resolvers.add(stubResolver(null, LongValueClass::class.java))
val method = ValueClassController::valueClassWithNullable.javaMethod!!
val result = invoke(ValueClassController(), method, null)
assertHandlerResultValue(result, "null")
}
@Test @Test
fun propertyAccessor() { fun propertyAccessor() {
this.resolvers.add(stubResolver(null, String::class.java)) this.resolvers.add(stubResolver(null, String::class.java))
@ -321,9 +336,13 @@ class InvocableHandlerMethodKotlinTests {
fun unit() { fun unit() {
} }
fun nullable(): String? { fun nullableReturnValue(): String? {
return null return null
} }
fun nullableParameter(value: String?): String? {
return value
}
} }
class ValueClassController { class ValueClassController {
@ -337,6 +356,9 @@ class InvocableHandlerMethodKotlinTests {
fun valueClassWithInit(valueClass: ValueClassWithInit) = fun valueClassWithInit(valueClass: ValueClassWithInit) =
valueClass valueClass
fun valueClassWithNullable(limit: LongValueClass?) =
"${limit?.value}"
} }
class PropertyAccessorController { class PropertyAccessorController {