diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java index 95fd5102626..3e2036d031e 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java @@ -16,6 +16,7 @@ package org.springframework.expression.spel; +import org.springframework.asm.MethodVisitor; import org.springframework.expression.EvaluationException; import org.springframework.expression.TypedValue; import org.springframework.lang.Nullable; @@ -25,6 +26,7 @@ import org.springframework.lang.Nullable; * Expression Language (SpEL) expression. * * @author Andy Clement + * @author Sam Brannen * @since 3.0 */ public interface SpelNode { @@ -109,4 +111,40 @@ public interface SpelNode { */ int getEndPosition(); + /** + * Determine if this node can be compiled to bytecode. + *
The reasoning in each node may be different but will typically involve + * checking whether the exit type descriptor of the node is known and any + * relevant child nodes are compilable. + *
The default implementation returns {@code false}. + *
If you override this method, you must also override + * {@link #generateCode(MethodVisitor, CodeFlow)}. + * @return {@code true} if this node can be compiled to bytecode + * @since 6.2 + * @see #generateCode(MethodVisitor, CodeFlow) + */ + default boolean isCompilable() { + return false; + } + + /** + * Generate the bytecode for this node into the supplied {@link MethodVisitor}. + *
Context information about the current expression being compiled is + * available in the supplied {@link CodeFlow} object — for example, + * information about the type of the object currently on the stack. + *
This method will not be invoked unless {@link #isCompilable()} returns + * {@code true}. + *
The default implementation throws an {@link IllegalStateException} + * since {@link #isCompilable()} returns {@code false} by default. + *
If you override this method, you must also override {@link #isCompilable()}. + * @param methodVisitor the ASM {@code MethodVisitor} into which code should + * be generated + * @param codeFlow a context object with information about what is on the stack + * @since 6.2 + * @see #isCompilable() + */ + default void generateCode(MethodVisitor methodVisitor, CodeFlow codeFlow) { + throw new IllegalStateException(getClass().getName() + " does not support bytecode generation"); + } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java index 3fb9208bc1b..a46a130f0a8 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java @@ -191,27 +191,6 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes { return false; } - /** - * Check whether a node can be compiled to bytecode. The reasoning in each node may - * be different but will typically involve checking whether the exit type descriptor - * of the node is known and any relevant child nodes are compilable. - * @return {@code true} if this node can be compiled to bytecode - */ - public boolean isCompilable() { - return false; - } - - /** - * Generate the bytecode for this node into the supplied visitor. Context info about - * the current expression being compiled is available in the codeflow object, e.g. - * including information about the type of the object currently on the stack. - * @param mv the ASM MethodVisitor into which code should be generated - * @param cf a context object with info about what is on the stack - */ - public void generateCode(MethodVisitor mv, CodeFlow cf) { - throw new IllegalStateException(getClass().getName() +" has no generateCode(..) method"); - } - @Nullable public String getExitDescriptor() { return this.exitTypeDescriptor;