-
Notifications
You must be signed in to change notification settings - Fork 38.9k
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
Reactions are currently unavailable
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