Skip to content

Commit b2f4d09

Browse files
authored
Implement Kotlin reflection calls in Java (#4562)
To avoid problems when working on the codebase with Eclipse.
1 parent fd0ca15 commit b2f4d09

File tree

4 files changed

+114
-65
lines changed

4 files changed

+114
-65
lines changed

junit-platform-commons/src/main/java/org/junit/platform/commons/util/KotlinReflectionUtils.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,29 +74,29 @@ private static boolean isKotlinType(Class<?> clazz) {
7474

7575
public static Class<?> getKotlinSuspendingFunctionReturnType(Method method) {
7676
requireKotlinReflect(method);
77-
return KotlinReflectionUtilsKt.getReturnType(method);
77+
return KotlinSuspendingFunctionUtils.getReturnType(method);
7878
}
7979

8080
public static Type getKotlinSuspendingFunctionGenericReturnType(Method method) {
8181
requireKotlinReflect(method);
82-
return KotlinReflectionUtilsKt.getGenericReturnType(method);
82+
return KotlinSuspendingFunctionUtils.getGenericReturnType(method);
8383
}
8484

8585
public static Parameter[] getKotlinSuspendingFunctionParameters(Method method) {
8686
requireKotlinReflect(method);
87-
return KotlinReflectionUtilsKt.getParameters(method);
87+
return KotlinSuspendingFunctionUtils.getParameters(method);
8888
}
8989

9090
public static Class<?>[] getKotlinSuspendingFunctionParameterTypes(Method method) {
9191
requireKotlinReflect(method);
92-
return KotlinReflectionUtilsKt.getParameterTypes(method);
92+
return KotlinSuspendingFunctionUtils.getParameterTypes(method);
9393
}
9494

9595
public static @Nullable Object invokeKotlinSuspendingFunction(Method method, @Nullable Object target,
9696
@Nullable Object[] args) {
9797
requireKotlinReflect(method);
9898
requireKotlinxCoroutines(method);
99-
return KotlinReflectionUtilsKt.invoke(method, target, args);
99+
return KotlinSuspendingFunctionUtils.invoke(method, target, args);
100100
}
101101

102102
private static void requireKotlinReflect(Method method) {
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2015-2025 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.platform.commons.util;
12+
13+
import static kotlin.jvm.JvmClassMappingKt.getJavaClass;
14+
import static kotlin.reflect.full.KCallables.callSuspendBy;
15+
import static kotlin.reflect.jvm.KCallablesJvm.isAccessible;
16+
import static kotlin.reflect.jvm.KCallablesJvm.setAccessible;
17+
import static kotlin.reflect.jvm.KTypesJvm.getJvmErasure;
18+
import static kotlin.reflect.jvm.ReflectJvmMapping.getJavaType;
19+
import static kotlinx.coroutines.BuildersKt.runBlocking;
20+
import static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;
21+
import static org.junit.platform.commons.util.ReflectionUtils.getUnderlyingCause;
22+
23+
import java.lang.reflect.Method;
24+
import java.lang.reflect.Parameter;
25+
import java.lang.reflect.Type;
26+
import java.util.Arrays;
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
30+
import kotlin.Unit;
31+
import kotlin.coroutines.EmptyCoroutineContext;
32+
import kotlin.reflect.KFunction;
33+
import kotlin.reflect.KParameter;
34+
import kotlin.reflect.jvm.ReflectJvmMapping;
35+
36+
class KotlinSuspendingFunctionUtils {
37+
38+
static Class<?> getReturnType(Method method) {
39+
var returnType = getJavaClass(getJvmErasure(getKotlinFunction(method).getReturnType()));
40+
if (Unit.class.equals(returnType)) {
41+
return void.class;
42+
}
43+
return returnType;
44+
}
45+
46+
static Type getGenericReturnType(Method method) {
47+
return getJavaType(getKotlinFunction(method).getReturnType());
48+
}
49+
50+
static Parameter[] getParameters(Method method) {
51+
var parameterCount = method.getParameterCount();
52+
if (parameterCount == 1) {
53+
return new Parameter[0];
54+
}
55+
return Arrays.copyOf(method.getParameters(), parameterCount - 1);
56+
}
57+
58+
static Class<?>[] getParameterTypes(Method method) {
59+
var parameterCount = method.getParameterCount();
60+
if (parameterCount == 1) {
61+
return new Class<?>[0];
62+
}
63+
return Arrays.stream(method.getParameterTypes()).limit(parameterCount - 1).toArray(Class<?>[]::new);
64+
}
65+
66+
static Object invoke(Method method, Object target, Object[] args) {
67+
try {
68+
return invoke(getKotlinFunction(method), target, args);
69+
}
70+
catch (InterruptedException e) {
71+
throw throwAsUncheckedException(e);
72+
}
73+
}
74+
75+
private static <T> T invoke(KFunction<T> function, Object target, Object[] args) throws InterruptedException {
76+
if (!isAccessible(function)) {
77+
setAccessible(function, true);
78+
}
79+
return runBlocking(EmptyCoroutineContext.INSTANCE, (__, continuation) -> {
80+
try {
81+
return callSuspendBy(function, toArgumentMap(target, args, function), continuation);
82+
}
83+
catch (Exception e) {
84+
throw throwAsUncheckedException(getUnderlyingCause(e));
85+
}
86+
});
87+
}
88+
89+
private static Map<KParameter, Object> toArgumentMap(Object target, Object[] args, KFunction<?> function) {
90+
Map<KParameter, Object> arguments = new HashMap<>(args.length + 1);
91+
int index = 0;
92+
for (KParameter parameter : function.getParameters()) {
93+
switch (parameter.getKind()) {
94+
case INSTANCE -> arguments.put(parameter, target);
95+
case VALUE, EXTENSION_RECEIVER -> {
96+
arguments.put(parameter, args[index]);
97+
index++;
98+
}
99+
}
100+
}
101+
return arguments;
102+
}
103+
104+
private static KFunction<?> getKotlinFunction(Method method) {
105+
return Preconditions.notNull(ReflectJvmMapping.getKotlinFunction(method),
106+
() -> "Failed to get Kotlin function for method: " + method);
107+
}
108+
}

junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1977,7 +1977,7 @@ private static boolean isSearchable(@Nullable Class<?> clazz) {
19771977
* {@linkplain InvocationTargetException#getTargetException() target
19781978
* exception}; otherwise, this method returns the supplied {@code Throwable}.
19791979
*/
1980-
private static Throwable getUnderlyingCause(Throwable t) {
1980+
static Throwable getUnderlyingCause(Throwable t) {
19811981
if (t instanceof InvocationTargetException) {
19821982
return getUnderlyingCause(((InvocationTargetException) t).getTargetException());
19831983
}

junit-platform-commons/src/main/kotlin/org/junit/platform/commons/util/KotlinReflectionUtils.kt

Lines changed: 0 additions & 59 deletions
This file was deleted.

0 commit comments

Comments
 (0)