Refine KotlinDetector usages and implementation
This commit refines KotlinDetector usages and implementation in order to remove preliminary KotlinDetector#isKotlinReflectPresent invocations and to ensure that KotlinDetector methods are implemented safely and efficiently for such use case. Closes gh-34275
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,7 +24,8 @@ import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A common delegate for detecting Kotlin's presence and for identifying Kotlin types.
|
||||
* A common delegate for detecting Kotlin's presence and for identifying Kotlin types. All the methods of this class
|
||||
* can be safely used without any preliminary classpath checks.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sebastien Deleuze
|
||||
@@ -37,6 +38,8 @@ public abstract class KotlinDetector {
|
||||
|
||||
private static final @Nullable Class<? extends Annotation> kotlinJvmInline;
|
||||
|
||||
private static final @Nullable Class<?> kotlinCoroutineContinuation;
|
||||
|
||||
// For ConstantFieldFeature compliance, otherwise could be deduced from kotlinMetadata
|
||||
private static final boolean kotlinPresent;
|
||||
|
||||
@@ -46,6 +49,7 @@ public abstract class KotlinDetector {
|
||||
ClassLoader classLoader = KotlinDetector.class.getClassLoader();
|
||||
Class<?> metadata = null;
|
||||
Class<?> jvmInline = null;
|
||||
Class<?> coroutineContinuation = null;
|
||||
try {
|
||||
metadata = ClassUtils.forName("kotlin.Metadata", classLoader);
|
||||
try {
|
||||
@@ -54,14 +58,21 @@ public abstract class KotlinDetector {
|
||||
catch (ClassNotFoundException ex) {
|
||||
// JVM inline support not available
|
||||
}
|
||||
try {
|
||||
coroutineContinuation = ClassUtils.forName("kotlin.coroutines.Continuation", classLoader);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// Coroutines support not available
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// Kotlin API not available - no Kotlin support
|
||||
}
|
||||
kotlinMetadata = (Class<? extends Annotation>) metadata;
|
||||
kotlinPresent = (kotlinMetadata != null);
|
||||
kotlinReflectPresent = kotlinPresent && ClassUtils.isPresent("kotlin.reflect.full.KClasses", classLoader);
|
||||
kotlinReflectPresent = ClassUtils.isPresent("kotlin.reflect.full.KClasses", classLoader);
|
||||
kotlinJvmInline = (Class<? extends Annotation>) jvmInline;
|
||||
kotlinCoroutineContinuation = coroutineContinuation;
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +100,7 @@ public abstract class KotlinDetector {
|
||||
* as invokedynamic has become the default method for lambda generation.
|
||||
*/
|
||||
public static boolean isKotlinType(Class<?> clazz) {
|
||||
return (kotlinMetadata != null && clazz.getDeclaredAnnotation(kotlinMetadata) != null);
|
||||
return (kotlinPresent && clazz.getDeclaredAnnotation(kotlinMetadata) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,13 +108,11 @@ public abstract class KotlinDetector {
|
||||
* @since 5.3
|
||||
*/
|
||||
public static boolean isSuspendingFunction(Method method) {
|
||||
if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
if (types.length > 0 && "kotlin.coroutines.Continuation".equals(types[types.length - 1].getName())) {
|
||||
return true;
|
||||
}
|
||||
if (kotlinCoroutineContinuation == null) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
int parameterCount = method.getParameterCount();
|
||||
return (parameterCount > 0 && method.getParameterTypes()[parameterCount - 1] == kotlinCoroutineContinuation);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -396,9 +396,7 @@ public class MethodParameter {
|
||||
*/
|
||||
public boolean isOptional() {
|
||||
return (getParameterType() == Optional.class || hasNullableAnnotation() ||
|
||||
(KotlinDetector.isKotlinReflectPresent() &&
|
||||
KotlinDetector.isKotlinType(getContainingClass()) &&
|
||||
KotlinDelegate.isOptional(this)));
|
||||
(KotlinDetector.isKotlinType(getContainingClass()) && KotlinDelegate.isOptional(this)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -508,8 +506,8 @@ public class MethodParameter {
|
||||
if (this.parameterIndex < 0) {
|
||||
Method method = getMethod();
|
||||
paramType = (method != null ?
|
||||
(KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(getContainingClass()) ?
|
||||
KotlinDelegate.getGenericReturnType(method) : method.getGenericReturnType()) : void.class);
|
||||
(KotlinDetector.isKotlinType(getContainingClass()) ?
|
||||
KotlinDelegate.getGenericReturnType(method) : method.getGenericReturnType()) : void.class);
|
||||
}
|
||||
else {
|
||||
Type[] genericParameterTypes = this.executable.getGenericParameterTypes();
|
||||
@@ -536,7 +534,7 @@ public class MethodParameter {
|
||||
if (method == null) {
|
||||
return void.class;
|
||||
}
|
||||
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(getContainingClass())) {
|
||||
if (KotlinDetector.isKotlinType(getContainingClass())) {
|
||||
return KotlinDelegate.getReturnType(method);
|
||||
}
|
||||
return method.getReturnType();
|
||||
|
||||
@@ -374,7 +374,7 @@ public class SpringFactoriesLoader {
|
||||
|
||||
T instantiate(@Nullable ArgumentResolver argumentResolver) throws Exception {
|
||||
Object[] args = resolveArgs(argumentResolver);
|
||||
if (isKotlinType(this.constructor.getDeclaringClass())) {
|
||||
if (KotlinDetector.isKotlinType(this.constructor.getDeclaringClass())) {
|
||||
return KotlinDelegate.instantiate(this.constructor, args);
|
||||
}
|
||||
return this.constructor.newInstance(args);
|
||||
@@ -408,14 +408,10 @@ public class SpringFactoriesLoader {
|
||||
}
|
||||
|
||||
private static @Nullable Constructor<?> findPrimaryKotlinConstructor(Class<?> factoryImplementationClass) {
|
||||
return (isKotlinType(factoryImplementationClass) ?
|
||||
return (KotlinDetector.isKotlinType(factoryImplementationClass) ?
|
||||
KotlinDelegate.findPrimaryConstructor(factoryImplementationClass) : null);
|
||||
}
|
||||
|
||||
private static boolean isKotlinType(Class<?> factoryImplementationClass) {
|
||||
return KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(factoryImplementationClass);
|
||||
}
|
||||
|
||||
private static @Nullable Constructor<?> findSingleConstructor(Constructor<?>[] constructors) {
|
||||
return (constructors.length == 1 ? constructors[0] : null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user