-
Notifications
You must be signed in to change notification settings - Fork 38.6k
Closed
Labels
in: coreIssues in core modules (aop, beans, core, context, expression)Issues in core modules (aop, beans, core, context, expression)type: enhancementA general enhancementA general enhancement
Milestone
Description
Overview
Somewhat analogous to the ReflectivePropertyAccessor
implementation of PropertyAccessor
, we should introduce a general purpose ReflectiveIndexAccessor
implementation as a convenience for users.
However, ReflectiveIndexAccessor
should implement CompilableIndexAccessor
(instead of just IndexAccessor
) in order to provide built-in compilation support.
A proof of concept has already been implemented in the tests for CompilableIndexAccessor
:
Lines 7238 to 7333 in 27d2200
/** | |
* {@link CompilableIndexAccessor} that uses reflection to invoke the | |
* configured read-method for index access operations. | |
*/ | |
static class ReflectiveIndexAccessor implements CompilableIndexAccessor { | |
private final Class<?> targetType; | |
private final Class<?> indexType; | |
private final Method readMethod; | |
private final Method readMethodToInvoke; | |
private final String targetTypeDesc; | |
private final String methodDescr; | |
public ReflectiveIndexAccessor(Class<?> targetType, Class<?> indexType, String readMethodName) { | |
this.targetType = targetType; | |
this.indexType = indexType; | |
this.readMethod = ReflectionUtils.findMethod(targetType, readMethodName, indexType); | |
Assert.notNull(this.readMethod, () -> "Failed to find method '%s(%s)' in class '%s'." | |
.formatted(readMethodName, indexType.getTypeName(), targetType.getTypeName())); | |
this.readMethodToInvoke = ClassUtils.getInterfaceMethodIfPossible(this.readMethod, targetType); | |
this.targetTypeDesc = CodeFlow.toDescriptor(targetType); | |
this.methodDescr = CodeFlow.createSignatureDescriptor(this.readMethod); | |
} | |
@Override | |
public Class<?>[] getSpecificTargetClasses() { | |
return new Class[] { this.targetType }; | |
} | |
@Override | |
public boolean canRead(EvaluationContext context, Object target, Object index) { | |
return (ClassUtils.isAssignableValue(this.targetType, target) && | |
ClassUtils.isAssignableValue(this.indexType, index)); | |
} | |
@Override | |
public TypedValue read(EvaluationContext context, Object target, Object index) { | |
ReflectionUtils.makeAccessible(this.readMethodToInvoke); | |
Object value = ReflectionUtils.invokeMethod(this.readMethodToInvoke, target, index); | |
return new TypedValue(value); | |
} | |
@Override | |
public boolean canWrite(EvaluationContext context, Object target, Object index) { | |
return false; | |
} | |
@Override | |
public void write(EvaluationContext context, Object target, Object index, @Nullable Object newValue) { | |
throw new UnsupportedOperationException(); | |
} | |
@Override | |
public boolean isCompilable() { | |
return true; | |
} | |
@Override | |
public Class<?> getIndexedValueType() { | |
return this.readMethod.getReturnType(); | |
} | |
@Override | |
public void generateCode(SpelNode index, MethodVisitor mv, CodeFlow cf) { | |
// Determine the public declaring class. | |
Class<?> publicDeclaringClass = this.readMethodToInvoke.getDeclaringClass(); | |
if (!Modifier.isPublic(publicDeclaringClass.getModifiers()) && this.readMethod != null) { | |
publicDeclaringClass = CodeFlow.findPublicDeclaringClass(this.readMethod); | |
} | |
Assert.state(publicDeclaringClass != null && Modifier.isPublic(publicDeclaringClass.getModifiers()), | |
() -> "Failed to find public declaring class for: " + this.readMethod); | |
// Ensure the current object on the stack is the target type. | |
String lastDesc = cf.lastDescriptor(); | |
if (lastDesc == null || !lastDesc.equals(this.targetTypeDesc)) { | |
CodeFlow.insertCheckCast(mv, this.targetTypeDesc); | |
} | |
// Push the index onto the stack. | |
cf.generateCodeForArgument(mv, index, this.indexType); | |
// Invoke the read method. | |
String classDesc = publicDeclaringClass.getName().replace('.', '/'); | |
boolean isStatic = Modifier.isStatic(this.readMethod.getModifiers()); | |
boolean isInterface = publicDeclaringClass.isInterface(); | |
int opcode = (isStatic ? INVOKESTATIC : isInterface ? INVOKEINTERFACE : INVOKEVIRTUAL); | |
mv.visitMethodInsn(opcode, classDesc, this.readMethod.getName(), this.methodDescr, isInterface); | |
} | |
} |
Related Issues
Dev-Gabriel-Martins
Metadata
Metadata
Assignees
Labels
in: coreIssues in core modules (aop, beans, core, context, expression)Issues in core modules (aop, beans, core, context, expression)type: enhancementA general enhancementA general enhancement