Merge branch '6.2.x'
This commit is contained in:
@@ -36,6 +36,7 @@ import kotlin.reflect.full.KClasses;
|
||||
import kotlin.reflect.jvm.KCallablesJvm;
|
||||
import kotlin.reflect.jvm.ReflectJvmMapping;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.SynchronousSink;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
|
||||
import org.springframework.core.CoroutinesUtils;
|
||||
@@ -323,18 +324,15 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
private static final String COROUTINE_CONTEXT_ATTRIBUTE = "org.springframework.web.server.CoWebFilter.context";
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"deprecation", "DataFlowIssue"})
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
public static Object invokeFunction(Method method, Object target, Object[] args, boolean isSuspendingFunction,
|
||||
ServerWebExchange exchange) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
|
||||
|
||||
if (isSuspendingFunction) {
|
||||
Object coroutineContext = exchange.getAttribute(COROUTINE_CONTEXT_ATTRIBUTE);
|
||||
if (coroutineContext == null) {
|
||||
return CoroutinesUtils.invokeSuspendingFunction(method, target, args);
|
||||
}
|
||||
else {
|
||||
return CoroutinesUtils.invokeSuspendingFunction((CoroutineContext) coroutineContext, method, target, args);
|
||||
}
|
||||
Object result = (coroutineContext == null ? CoroutinesUtils.invokeSuspendingFunction(method, target, args) :
|
||||
CoroutinesUtils.invokeSuspendingFunction((CoroutineContext) coroutineContext, method, target, args));
|
||||
return (result instanceof Mono<?> mono ? mono.handle(KotlinDelegate::handleResult) : result);
|
||||
}
|
||||
else {
|
||||
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
|
||||
@@ -370,11 +368,35 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
}
|
||||
Object result = function.callBy(argMap);
|
||||
if (result != null && KotlinDetector.isInlineClass(result.getClass())) {
|
||||
return result.getClass().getDeclaredMethod("unbox-impl").invoke(result);
|
||||
result = unbox(result);
|
||||
}
|
||||
return (result == Unit.INSTANCE ? null : result);
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleResult(Object result, SynchronousSink<Object> sink) {
|
||||
if (KotlinDetector.isInlineClass(result.getClass())) {
|
||||
try {
|
||||
Object unboxed = unbox(result);
|
||||
if (unboxed != Unit.INSTANCE) {
|
||||
sink.next(unboxed);
|
||||
}
|
||||
sink.complete();
|
||||
}
|
||||
catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
|
||||
sink.error(ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sink.next(result);
|
||||
sink.complete();
|
||||
}
|
||||
}
|
||||
|
||||
private static Object unbox(Object result) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
|
||||
return result.getClass().getDeclaredMethod("unbox-impl").invoke(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -208,10 +208,17 @@ class InvocableHandlerMethodKotlinTests {
|
||||
@Test
|
||||
fun valueClassReturnValue() {
|
||||
val method = ValueClassController::valueClassReturnValue.javaMethod!!
|
||||
val result = invoke(ValueClassController(), method,)
|
||||
val result = invoke(ValueClassController(), method)
|
||||
assertHandlerResultValue(result, "foo")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resultOfUnitReturnValue() {
|
||||
val method = ValueClassController::resultOfUnitReturnValue.javaMethod!!
|
||||
val result = invoke(ValueClassController(), method)
|
||||
assertHandlerResultValue(result, null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun valueClassWithDefaultValue() {
|
||||
this.resolvers.add(stubResolver(null, Double::class.java))
|
||||
@@ -244,6 +251,60 @@ class InvocableHandlerMethodKotlinTests {
|
||||
assertHandlerResultValue(result, "1")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClass() {
|
||||
this.resolvers.add(stubResolver(1L, Long::class.java))
|
||||
val method = SuspendingValueClassController::valueClass.javaMethod!!
|
||||
val result = invoke(SuspendingValueClassController(), method,1L)
|
||||
assertHandlerResultValue(result, "1")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClassReturnValue() {
|
||||
val method = SuspendingValueClassController::valueClassReturnValue.javaMethod!!
|
||||
val result = invoke(SuspendingValueClassController(), method)
|
||||
assertHandlerResultValue(result, "foo")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingResultOfUnitReturnValue() {
|
||||
val method = SuspendingValueClassController::resultOfUnitReturnValue.javaMethod!!
|
||||
val result = invoke(SuspendingValueClassController(), method)
|
||||
assertComplete(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClassWithDefaultValue() {
|
||||
this.resolvers.add(stubResolver(null, Double::class.java))
|
||||
val method = SuspendingValueClassController::valueClassWithDefault.javaMethod!!
|
||||
val result = invoke(SuspendingValueClassController(), method)
|
||||
assertHandlerResultValue(result, "3.1")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClassWithInit() {
|
||||
this.resolvers.add(stubResolver("", String::class.java))
|
||||
val method = SuspendingValueClassController::valueClassWithInit.javaMethod!!
|
||||
val result = invoke(SuspendingValueClassController(), method)
|
||||
assertExceptionThrown(result, IllegalArgumentException::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClassWithNullable() {
|
||||
this.resolvers.add(stubResolver(null, LongValueClass::class.java))
|
||||
val method = SuspendingValueClassController::valueClassWithNullable.javaMethod!!
|
||||
val result = invoke(SuspendingValueClassController(), method, null)
|
||||
assertHandlerResultValue(result, "null")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClassWithPrivateConstructor() {
|
||||
this.resolvers.add(stubResolver(1L, Long::class.java))
|
||||
val method = SuspendingValueClassController::valueClassWithPrivateConstructor.javaMethod!!
|
||||
val result = invoke(SuspendingValueClassController(), method, 1L)
|
||||
assertHandlerResultValue(result, "1")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun propertyAccessor() {
|
||||
this.resolvers.add(stubResolver(null, String::class.java))
|
||||
@@ -313,9 +374,14 @@ class InvocableHandlerMethodKotlinTests {
|
||||
}
|
||||
|
||||
private fun assertExceptionThrown(mono: Mono<HandlerResult>, exceptionClass: KClass<out Throwable>) {
|
||||
StepVerifier.create(mono).verifyError(exceptionClass.java)
|
||||
StepVerifier.create(mono.flatMap { t -> t.returnValue as Mono<*> }).verifyError(exceptionClass.java)
|
||||
}
|
||||
|
||||
private fun assertComplete(mono: Mono<HandlerResult>) {
|
||||
StepVerifier.create(mono.flatMap { t -> t.returnValue as Mono<*> }).verifyComplete()
|
||||
}
|
||||
|
||||
|
||||
class CoroutinesController {
|
||||
|
||||
suspend fun singleArg(q: String?): String {
|
||||
@@ -380,23 +446,57 @@ class InvocableHandlerMethodKotlinTests {
|
||||
|
||||
class ValueClassController {
|
||||
|
||||
fun valueClass(limit: LongValueClass) =
|
||||
"${limit.value}"
|
||||
fun valueClass(limit: LongValueClass) = "${limit.value}"
|
||||
|
||||
fun valueClassReturnValue() =
|
||||
StringValueClass("foo")
|
||||
fun valueClassReturnValue() = StringValueClass("foo")
|
||||
|
||||
fun valueClassWithDefault(limit: DoubleValueClass = DoubleValueClass(3.1)) =
|
||||
"${limit.value}"
|
||||
fun resultOfUnitReturnValue() = Result.success(Unit)
|
||||
|
||||
fun valueClassWithInit(valueClass: ValueClassWithInit) =
|
||||
valueClass
|
||||
fun valueClassWithDefault(limit: DoubleValueClass = DoubleValueClass(3.1)) = "${limit.value}"
|
||||
|
||||
fun valueClassWithNullable(limit: LongValueClass?) =
|
||||
"${limit?.value}"
|
||||
fun valueClassWithInit(valueClass: ValueClassWithInit) = valueClass
|
||||
|
||||
fun valueClassWithPrivateConstructor(limit: ValueClassWithPrivateConstructor) =
|
||||
"${limit.value}"
|
||||
fun valueClassWithNullable(limit: LongValueClass?) = "${limit?.value}"
|
||||
|
||||
fun valueClassWithPrivateConstructor(limit: ValueClassWithPrivateConstructor) = "${limit.value}"
|
||||
}
|
||||
|
||||
class SuspendingValueClassController {
|
||||
|
||||
suspend fun valueClass(limit: LongValueClass): String {
|
||||
delay(1)
|
||||
return "${limit.value}"
|
||||
}
|
||||
|
||||
suspend fun valueClassReturnValue(): StringValueClass {
|
||||
delay(1)
|
||||
return StringValueClass("foo")
|
||||
}
|
||||
|
||||
suspend fun resultOfUnitReturnValue(): Result<Unit> {
|
||||
delay(1)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
suspend fun valueClassWithDefault(limit: DoubleValueClass = DoubleValueClass(3.1)): String {
|
||||
delay(1)
|
||||
return "${limit.value}"
|
||||
}
|
||||
|
||||
suspend fun valueClassWithInit(valueClass: ValueClassWithInit): ValueClassWithInit {
|
||||
delay(1)
|
||||
return valueClass
|
||||
}
|
||||
|
||||
suspend fun valueClassWithNullable(limit: LongValueClass?): String {
|
||||
delay(1)
|
||||
return "${limit?.value}"
|
||||
}
|
||||
|
||||
suspend fun valueClassWithPrivateConstructor(limit: ValueClassWithPrivateConstructor): String {
|
||||
delay(1)
|
||||
return "${limit.value}"
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyAccessorController {
|
||||
|
||||
Reference in New Issue
Block a user