From 38c831f15fadef0f5ca4ecf032a5d4ccdb293e90 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:33:52 +0100 Subject: [PATCH] Relocate findPublicDeclaringClass() to CodeFlow This commit moves findPublicDeclaringClass() from ReflectionHelper to CodeFlow, since findPublicDeclaringClass() is only used for bytecode generation and therefore not for reflection-based invocations. --- .../expression/spel/CodeFlow.java | 75 ++++++++++++++++++- .../spel/support/ReflectionHelper.java | 71 ------------------ .../support/ReflectiveMethodExecutor.java | 5 +- .../support/ReflectivePropertyAccessor.java | 4 +- 4 files changed, 79 insertions(+), 76 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java index e6b57a31355..2f3e4468371 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java @@ -18,29 +18,43 @@ package org.springframework.expression.spel; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; +import java.util.Map; import org.springframework.asm.ClassWriter; import org.springframework.asm.MethodVisitor; import org.springframework.asm.Opcodes; import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; +import org.springframework.util.ConcurrentReferenceHashMap; /** * Manages the class being generated by the compilation process. * *
Records intermediate compilation state as the bytecode is generated. - * Also includes various bytecode generation helper functions. + * + *
Also includes various bytecode generation helper functions.
*
* @author Andy Clement
* @author Juergen Hoeller
+ * @author Sam Brannen
* @since 4.1
*/
public class CodeFlow implements Opcodes {
+ /**
+ * Cache for equivalent methods in a public declaring class in the type
+ * hierarchy of the method's declaring class.
+ * @since 6.2
+ */
+ private static final Map Sometimes the reflective method discovery logic finds a suitable method
+ * that can easily be called via reflection but cannot be called from generated
+ * code when compiling the expression because of visibility restrictions. For
+ * example, if a non-public class overrides {@code toString()}, this method
+ * will traverse up the type hierarchy to find the first public type that
+ * declares the method (if there is one). For {@code toString()}, it may
+ * traverse as far as {@link Object}.
+ * @param method the method to process
+ * @return the public class or interface that declares the method, or
+ * {@code null} if no such public type could be found
+ * @since 6.2
+ */
+ @Nullable
+ public static Class> findPublicDeclaringClass(Method method) {
+ return publicDeclaringClassCache.computeIfAbsent(method, key -> {
+ // If the method is already defined in a public type, return that type.
+ if (Modifier.isPublic(key.getDeclaringClass().getModifiers())) {
+ return key.getDeclaringClass();
+ }
+ Method interfaceMethod = ClassUtils.getInterfaceMethodIfPossible(key, null);
+ // If we found an interface method whose type is public, return the interface type.
+ if (!interfaceMethod.equals(key)) {
+ if (Modifier.isPublic(interfaceMethod.getDeclaringClass().getModifiers())) {
+ return interfaceMethod.getDeclaringClass();
+ }
+ }
+ // Attempt to search the type hierarchy.
+ Class> superclass = key.getDeclaringClass().getSuperclass();
+ if (superclass != null) {
+ return findPublicDeclaringClass(superclass, key.getName(), key.getParameterTypes());
+ }
+ // Otherwise, no public declaring class found.
+ return null;
+ });
+ }
+
+ @Nullable
+ private static Class> findPublicDeclaringClass(
+ Class> declaringClass, String methodName, Class>[] parameterTypes) {
+
+ if (Modifier.isPublic(declaringClass.getModifiers())) {
+ try {
+ declaringClass.getDeclaredMethod(methodName, parameterTypes);
+ return declaringClass;
+ }
+ catch (NoSuchMethodException ex) {
+ // Continue below...
+ }
+ }
+
+ Class> superclass = declaringClass.getSuperclass();
+ if (superclass != null) {
+ return findPublicDeclaringClass(superclass, methodName, parameterTypes);
+ }
+ return null;
+ }
/**
* Create the JVM signature descriptor for a method. This consists of the descriptors
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java
index 2df7a155a98..375f9f6163c 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java
@@ -21,9 +21,7 @@ import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import org.springframework.core.MethodParameter;
@@ -36,7 +34,6 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
-import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.MethodInvoker;
/**
@@ -50,14 +47,6 @@ import org.springframework.util.MethodInvoker;
*/
public abstract class ReflectionHelper {
- /**
- * Cache for equivalent methods in a public declaring class in the type
- * hierarchy of the method's declaring class.
- * @since 6.2
- */
- private static final Map A supplied type converter and conversionAllowed flag allow for matches to take
@@ -499,66 +488,6 @@ public abstract class ReflectionHelper {
return args;
}
- /**
- * Find the first public class or interface in the method's class hierarchy
- * that declares the supplied method.
- * Sometimes the reflective method discovery logic finds a suitable method
- * that can easily be called via reflection but cannot be called from generated
- * code when compiling the expression because of visibility restrictions. For
- * example, if a non-public class overrides {@code toString()}, this method
- * will traverse up the type hierarchy to find the first public type that
- * declares the method (if there is one). For {@code toString()}, it may
- * traverse as far as {@link Object}.
- * @param method the method to process
- * @return the public class or interface that declares the method, or
- * {@code null} if no such public type could be found
- * @since 6.2
- */
- @Nullable
- public static Class> findPublicDeclaringClass(Method method) {
- return publicDeclaringClassCache.computeIfAbsent(method, key -> {
- // If the method is already defined in a public type, return that type.
- if (Modifier.isPublic(key.getDeclaringClass().getModifiers())) {
- return key.getDeclaringClass();
- }
- Method interfaceMethod = ClassUtils.getInterfaceMethodIfPossible(key, null);
- // If we found an interface method whose type is public, return the interface type.
- if (!interfaceMethod.equals(key)) {
- if (Modifier.isPublic(interfaceMethod.getDeclaringClass().getModifiers())) {
- return interfaceMethod.getDeclaringClass();
- }
- }
- // Attempt to search the type hierarchy.
- Class> superclass = key.getDeclaringClass().getSuperclass();
- if (superclass != null) {
- return findPublicDeclaringClass(superclass, key.getName(), key.getParameterTypes());
- }
- // Otherwise, no public declaring class found.
- return null;
- });
- }
-
- @Nullable
- private static Class> findPublicDeclaringClass(
- Class> declaringClass, String methodName, Class>[] parameterTypes) {
-
- if (Modifier.isPublic(declaringClass.getModifiers())) {
- try {
- declaringClass.getDeclaredMethod(methodName, parameterTypes);
- return declaringClass;
- }
- catch (NoSuchMethodException ex) {
- // Continue below...
- }
- }
-
- Class> superclass = declaringClass.getSuperclass();
- if (superclass != null) {
- return findPublicDeclaringClass(superclass, methodName, parameterTypes);
- }
- return null;
- }
-
/**
* Arguments match kinds.
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java
index 8b93e69135b..f93bea65503 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java
@@ -24,6 +24,7 @@ import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.MethodExecutor;
import org.springframework.expression.TypedValue;
+import org.springframework.expression.spel.CodeFlow;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@@ -93,7 +94,7 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
/**
* Find a public class or interface in the method's class hierarchy that
* declares the {@linkplain #getMethod() original method}.
- * See {@link ReflectionHelper#findPublicDeclaringClass(Method)} for
+ * See {@link CodeFlow#findPublicDeclaringClass(Method)} for
* details.
* @return the public class or interface that declares the method, or
* {@code null} if no such public type could be found
@@ -101,7 +102,7 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
@Nullable
public Class> getPublicDeclaringClass() {
if (!this.computedPublicDeclaringClass) {
- this.publicDeclaringClass = ReflectionHelper.findPublicDeclaringClass(this.originalMethod);
+ this.publicDeclaringClass = CodeFlow.findPublicDeclaringClass(this.originalMethod);
this.computedPublicDeclaringClass = true;
}
return this.publicDeclaringClass;
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java
index fa7e2f78fd0..115a3c41277 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java
@@ -698,7 +698,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return true;
}
if (this.originalMethod != null) {
- return (ReflectionHelper.findPublicDeclaringClass(this.originalMethod) != null);
+ return (CodeFlow.findPublicDeclaringClass(this.originalMethod) != null);
}
return false;
}
@@ -717,7 +717,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) {
Class> publicDeclaringClass = this.member.getDeclaringClass();
if (!Modifier.isPublic(publicDeclaringClass.getModifiers()) && this.originalMethod != null) {
- publicDeclaringClass = ReflectionHelper.findPublicDeclaringClass(this.originalMethod);
+ publicDeclaringClass = CodeFlow.findPublicDeclaringClass(this.originalMethod);
}
Assert.state(publicDeclaringClass != null && Modifier.isPublic(publicDeclaringClass.getModifiers()),
() -> "Failed to find public declaring class for: " +