mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-10-06 23:19:03 +00:00
Avoid Kotlin reflection for non-suspending return types
Previously, KotlinDelegate.getReturnType() and getGenericReturnType() resolved the KFunction of every Kotlin method, which materializes the members of the declaring KClass and is costly at startup. Kotlin reflection is only needed to resolve the return type of suspending functions, so both methods now only use it when KotlinDetector.isSuspendingFunction() returns true. Closes gh-37300 Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
This commit is contained in:
@@ -972,14 +972,16 @@ public class MethodParameter {
|
||||
* functions via Kotlin reflection.
|
||||
*/
|
||||
private static Type getGenericReturnType(Method method) {
|
||||
try {
|
||||
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
|
||||
if (function != null && function.isSuspend()) {
|
||||
return ReflectJvmMapping.getJavaType(function.getReturnType());
|
||||
if (KotlinDetector.isSuspendingFunction(method)) {
|
||||
try {
|
||||
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
|
||||
if (function != null && function.isSuspend()) {
|
||||
return ReflectJvmMapping.getJavaType(function.getReturnType());
|
||||
}
|
||||
}
|
||||
catch (UnsupportedOperationException ex) {
|
||||
// probably a synthetic class - let's use java reflection instead
|
||||
}
|
||||
}
|
||||
catch (UnsupportedOperationException ex) {
|
||||
// probably a synthetic class - let's use java reflection instead
|
||||
}
|
||||
return method.getGenericReturnType();
|
||||
}
|
||||
@@ -989,18 +991,20 @@ public class MethodParameter {
|
||||
* functions via Kotlin reflection.
|
||||
*/
|
||||
private static Class<?> getReturnType(Method method) {
|
||||
try {
|
||||
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
|
||||
if (function != null && function.isSuspend()) {
|
||||
Type paramType = ReflectJvmMapping.getJavaType(function.getReturnType());
|
||||
if (paramType == Unit.class) {
|
||||
paramType = void.class;
|
||||
if (KotlinDetector.isSuspendingFunction(method)) {
|
||||
try {
|
||||
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
|
||||
if (function != null && function.isSuspend()) {
|
||||
Type paramType = ReflectJvmMapping.getJavaType(function.getReturnType());
|
||||
if (paramType == Unit.class) {
|
||||
paramType = void.class;
|
||||
}
|
||||
return ResolvableType.forType(paramType).resolve(method.getReturnType());
|
||||
}
|
||||
return ResolvableType.forType(paramType).resolve(method.getReturnType());
|
||||
}
|
||||
}
|
||||
catch (UnsupportedOperationException ex) {
|
||||
// probably a synthetic class - let's use java reflection instead
|
||||
catch (UnsupportedOperationException ex) {
|
||||
// probably a synthetic class - let's use java reflection instead
|
||||
}
|
||||
}
|
||||
return method.getReturnType();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.core
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito.mockStatic
|
||||
import org.mockito.stubbing.Answer
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.TypeVariable
|
||||
import kotlin.coroutines.Continuation
|
||||
@@ -114,6 +116,30 @@ class MethodParameterKotlinTests {
|
||||
assertThat(returnGenericParameterType("suspendFun8")).isEqualTo(Any::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Regular function return type`() {
|
||||
assertThat(returnParameterType("regularFun")).isEqualTo(Producer::class.java)
|
||||
assertThat(returnGenericParameterTypeName("regularFun")).isEqualTo("org.springframework.core.Producer<java.lang.Number>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Regular function return type does not use Kotlin reflection`() {
|
||||
val methodParameter = returnMethodParameter("regularFun")
|
||||
mockStatic(Class.forName("kotlin.reflect.jvm.ReflectJvmMapping"), Answer<Any> {
|
||||
throw AssertionError("Kotlin reflection should not be used for regular functions")
|
||||
}).use {
|
||||
assertThat(methodParameter.parameterType).isEqualTo(Producer::class.java)
|
||||
assertThat(methodParameter.genericParameterType.typeName)
|
||||
.isEqualTo("org.springframework.core.Producer<java.lang.Number>")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Regular function with Continuation parameter return type`() {
|
||||
assertThat(returnParameterType("regularFunWithContinuation")).isEqualTo(Producer::class.java)
|
||||
assertThat(returnGenericParameterTypeName("regularFunWithContinuation")).isEqualTo("org.springframework.core.Producer<java.lang.Number>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Parameter name for regular function`() {
|
||||
val methodParameter = returnMethodParameter("nullable", 0)
|
||||
@@ -186,6 +212,12 @@ class MethodParameterKotlinTests {
|
||||
|
||||
@Suppress("unused", "unused_parameter")
|
||||
suspend fun suspendFun8(p1: String): Any? = TODO()
|
||||
|
||||
@Suppress("unused", "unused_parameter")
|
||||
fun regularFun(p1: String): Producer<Number> = TODO()
|
||||
|
||||
@Suppress("unused", "unused_parameter")
|
||||
fun regularFunWithContinuation(p1: String, continuation: Continuation<Number>): Producer<Number> = TODO()
|
||||
}
|
||||
|
||||
interface Producer<out T>
|
||||
|
||||
Reference in New Issue
Block a user