@ -18,29 +18,43 @@ package org.springframework.expression.spel;
@@ -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 .
*
* < p > Records intermediate compilation state as the bytecode is generated .
* Also includes various bytecode generation helper functions .
*
* < p > 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 < Method , Class < ? > > publicDeclaringClassCache = new ConcurrentReferenceHashMap < > ( 256 ) ;
/ * *
* Name of the class being generated . Typically used when generating code
* that accesses freshly generated fields on the generated type .
@ -395,6 +409,65 @@ public class CodeFlow implements Opcodes {
@@ -395,6 +409,65 @@ public class CodeFlow implements Opcodes {
}
}
/ * *
* Find the first public class or interface in the method ' s class hierarchy
* that declares the supplied method .
* < p > 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