Skip to content

Commit fd38c23

Browse files
committed
Refine CoroutinesUtils#invokeSuspendingFunction contract
This commit refines CoroutinesUtils#invokeSuspendingFunction in order to clarify the behavior when used on a non suspending function, and support usages with or without the Continuation argument. Closes gh-30005
1 parent e47418c commit fd38c23

File tree

1 file changed

+16
-10
lines changed

1 file changed

+16
-10
lines changed

spring-core/src/main/java/org/springframework/core/CoroutinesUtils.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
import reactor.core.publisher.Flux;
4242
import reactor.core.publisher.Mono;
4343

44+
import org.springframework.util.Assert;
45+
4446
/**
4547
* Utilities for working with Kotlin Coroutines.
4648
*
@@ -68,13 +70,14 @@ public static <T> Deferred<T> monoToDeferred(Mono<T> source) {
6870
}
6971

7072
/**
71-
* Invoke a suspending function and converts it to {@link Mono} or
72-
* {@link Flux}. Uses an {@linkplain Dispatchers#getUnconfined() unconfined}
73-
* dispatcher.
73+
* Invoke a suspending function and converts it to {@link Mono} or {@link Flux}.
74+
* Uses an {@linkplain Dispatchers#getUnconfined() unconfined} dispatcher.
7475
* @param method the suspending function to invoke
7576
* @param target the target to invoke {@code method} on
76-
* @param args the function arguments
77+
* @param args the function arguments. If the {@code Continuation} argument is specified as the last argument
78+
* (typically {@code null}), it is ignored.
7779
* @return the method invocation result as reactive stream
80+
* @throws IllegalArgumentException if {@code method} is not a suspending function
7881
*/
7982
public static Publisher<?> invokeSuspendingFunction(Method method, Object target,
8083
Object... args) {
@@ -87,20 +90,22 @@ public static Publisher<?> invokeSuspendingFunction(Method method, Object target
8790
* @param context the coroutine context to use
8891
* @param method the suspending function to invoke
8992
* @param target the target to invoke {@code method} on
90-
* @param args the function arguments
93+
* @param args the function arguments. If the {@code Continuation} argument is specified as the last argument
94+
* (typically {@code null}), it is ignored.
9195
* @return the method invocation result as reactive stream
96+
* @throws IllegalArgumentException if {@code method} is not a suspending function
9297
* @since 6.0
9398
*/
9499
@SuppressWarnings("deprecation")
95100
public static Publisher<?> invokeSuspendingFunction(CoroutineContext context, Method method, Object target,
96101
Object... args) {
97-
102+
Assert.isTrue(KotlinDetector.isSuspendingFunction(method), "'method' must be a suspending function");
98103
KFunction<?> function = Objects.requireNonNull(ReflectJvmMapping.getKotlinFunction(method));
99104
if (method.isAccessible() && !KCallablesJvm.isAccessible(function)) {
100105
KCallablesJvm.setAccessible(function, true);
101106
}
102107
Mono<Object> mono = MonoKt.mono(context, (scope, continuation) ->
103-
KCallables.callSuspend(function, getSuspendedFunctionArgs(target, args), continuation))
108+
KCallables.callSuspend(function, getSuspendedFunctionArgs(method, target, args), continuation))
104109
.filter(result -> !Objects.equals(result, Unit.INSTANCE))
105110
.onErrorMap(InvocationTargetException.class, InvocationTargetException::getTargetException);
106111

@@ -120,10 +125,11 @@ else if (returnType instanceof KClass<?> kClass &&
120125
return mono;
121126
}
122127

123-
private static Object[] getSuspendedFunctionArgs(Object target, Object... args) {
124-
Object[] functionArgs = new Object[args.length];
128+
private static Object[] getSuspendedFunctionArgs(Method method, Object target, Object... args) {
129+
int length = (args.length == method.getParameterCount() - 1 ? args.length + 1 : args.length);
130+
Object[] functionArgs = new Object[length];
125131
functionArgs[0] = target;
126-
System.arraycopy(args, 0, functionArgs, 1, args.length - 1);
132+
System.arraycopy(args, 0, functionArgs, 1, length - 1);
127133
return functionArgs;
128134
}
129135

0 commit comments

Comments
 (0)