1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.expression.spel.ast;
18
19 import java.lang.reflect.Method;
20 import java.lang.reflect.Modifier;
21
22 import org.springframework.asm.MethodVisitor;
23 import org.springframework.core.MethodParameter;
24 import org.springframework.core.convert.TypeDescriptor;
25 import org.springframework.expression.EvaluationException;
26 import org.springframework.expression.TypeConverter;
27 import org.springframework.expression.TypedValue;
28 import org.springframework.expression.spel.CodeFlow;
29 import org.springframework.expression.spel.ExpressionState;
30 import org.springframework.expression.spel.SpelEvaluationException;
31 import org.springframework.expression.spel.SpelMessage;
32 import org.springframework.expression.spel.support.ReflectionHelper;
33 import org.springframework.util.ReflectionUtils;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public class FunctionReference extends SpelNodeImpl {
50
51 private final String name;
52
53
54
55 private Method method;
56
57 private boolean argumentConversionOccurred;
58
59
60 public FunctionReference(String functionName, int pos, SpelNodeImpl... arguments) {
61 super(pos,arguments);
62 this.name = functionName;
63 }
64
65
66 @Override
67 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
68 TypedValue value = state.lookupVariable(this.name);
69 if (value == null) {
70 throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_NOT_DEFINED, this.name);
71 }
72
73
74 if (!(value.getValue() instanceof Method)) {
75 throw new SpelEvaluationException(SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, this.name, value.getClass());
76 }
77
78 try {
79 return executeFunctionJLRMethod(state, (Method) value.getValue());
80 }
81 catch (SpelEvaluationException ex) {
82 ex.setPosition(getStartPosition());
83 throw ex;
84 }
85 }
86
87
88
89
90
91
92
93
94 private TypedValue executeFunctionJLRMethod(ExpressionState state, Method method) throws EvaluationException {
95 this.method = null;
96 Object[] functionArgs = getArguments(state);
97
98 if (!method.isVarArgs() && method.getParameterTypes().length != functionArgs.length) {
99 throw new SpelEvaluationException(SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION,
100 functionArgs.length, method.getParameterTypes().length);
101 }
102
103 if (!Modifier.isStatic(method.getModifiers())) {
104 throw new SpelEvaluationException(getStartPosition(),
105 SpelMessage.FUNCTION_MUST_BE_STATIC,
106 method.getDeclaringClass().getName() + "." + method.getName(), this.name);
107 }
108
109 argumentConversionOccurred = false;
110
111 if (functionArgs != null) {
112 TypeConverter converter = state.getEvaluationContext().getTypeConverter();
113 argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
114 }
115 if (method.isVarArgs()) {
116 functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(method.getParameterTypes(), functionArgs);
117 }
118
119 try {
120 ReflectionUtils.makeAccessible(method);
121 Object result = method.invoke(method.getClass(), functionArgs);
122 if (!argumentConversionOccurred) {
123 this.method = method;
124 this.exitTypeDescriptor = CodeFlow.toDescriptor(method.getReturnType());
125 }
126 return new TypedValue(result, new TypeDescriptor(new MethodParameter(method, -1)).narrow(result));
127 }
128 catch (Exception ex) {
129 throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL,
130 this.name, ex.getMessage());
131 }
132 }
133
134 @Override
135 public String toStringAST() {
136 StringBuilder sb = new StringBuilder("#").append(this.name);
137 sb.append("(");
138 for (int i = 0; i < getChildCount(); i++) {
139 if (i > 0) {
140 sb.append(",");
141 }
142 sb.append(getChild(i).toStringAST());
143 }
144 sb.append(")");
145 return sb.toString();
146 }
147
148
149
150
151
152 private Object[] getArguments(ExpressionState state) throws EvaluationException {
153
154 Object[] arguments = new Object[getChildCount()];
155 for (int i = 0; i < arguments.length; i++) {
156 arguments[i] = this.children[i].getValueInternal(state).getValue();
157 }
158 return arguments;
159 }
160
161 @Override
162 public boolean isCompilable() {
163 if (this.method == null || argumentConversionOccurred) {
164 return false;
165 }
166 int methodModifiers = this.method.getModifiers();
167 if (!Modifier.isStatic(methodModifiers) ||
168 !Modifier.isPublic(methodModifiers) ||
169 !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
170 return false;
171 }
172 for (SpelNodeImpl child : this.children) {
173 if (!child.isCompilable()) {
174 return false;
175 }
176 }
177 return true;
178 }
179
180 @Override
181 public void generateCode(MethodVisitor mv,CodeFlow cf) {
182 String methodDeclaringClassSlashedDescriptor = this.method.getDeclaringClass().getName().replace('.', '/');
183 generateCodeForArguments(mv, cf, method, this.children);
184 mv.visitMethodInsn(INVOKESTATIC, methodDeclaringClassSlashedDescriptor, this.method.getName(),
185 CodeFlow.createSignatureDescriptor(this.method), false);
186 cf.pushDescriptor(this.exitTypeDescriptor);
187 }
188
189 }