View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.expression.spel.ast;
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.Member;
21  import java.lang.reflect.Method;
22  
23  import org.springframework.asm.MethodVisitor;
24  import org.springframework.asm.Opcodes;
25  import org.springframework.expression.EvaluationException;
26  import org.springframework.expression.TypedValue;
27  import org.springframework.expression.common.ExpressionUtils;
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.SpelNode;
33  import org.springframework.expression.spel.support.StandardEvaluationContext;
34  import org.springframework.util.Assert;
35  import org.springframework.util.ObjectUtils;
36  
37  /**
38   * The common supertype of all AST nodes in a parsed Spring Expression Language
39   * format expression.
40   *
41   * @author Andy Clement
42   * @since 3.0
43   */
44  public abstract class SpelNodeImpl implements SpelNode, Opcodes {
45  
46  	private static SpelNodeImpl[] NO_CHILDREN = new SpelNodeImpl[0];
47  
48  
49  	protected int pos; // start = top 16bits, end = bottom 16bits
50  
51  	protected SpelNodeImpl[] children = SpelNodeImpl.NO_CHILDREN;
52  
53  	private SpelNodeImpl parent;
54  
55  	/**
56  	 * Indicates the type descriptor for the result of this expression node.
57  	 * This is set as soon as it is known. For a literal node it is known immediately.
58  	 * For a property access or method invocation it is known after one evaluation of
59  	 * that node.
60  	 * <p>The descriptor is like the bytecode form but is slightly easier to work with.
61  	 * It does not include the trailing semicolon (for non array reference types).
62  	 * Some examples: Ljava/lang/String, I, [I
63       */
64  	protected volatile String exitTypeDescriptor;
65  
66  
67  	public SpelNodeImpl(int pos, SpelNodeImpl... operands) {
68  		this.pos = pos;
69  		// pos combines start and end so can never be zero because tokens cannot be zero length
70  		Assert.isTrue(pos != 0, "Pos must not be 0");
71  		if (!ObjectUtils.isEmpty(operands)) {
72  			this.children = operands;
73  			for (SpelNodeImpl childNode : operands) {
74  				childNode.parent = this;
75  			}
76  		}
77  	}
78  
79  
80  	protected SpelNodeImpl getPreviousChild() {
81  		SpelNodeImpl result = null;
82  		if (this.parent != null) {
83  			for (SpelNodeImpl child : this.parent.children) {
84  				if (this == child) {
85  					break;
86  				}
87  				result = child;
88  			}
89  		}
90  		return result;
91  	}
92  
93  	/**
94       * @return true if the next child is one of the specified classes
95       */
96  	protected boolean nextChildIs(Class<?>... clazzes) {
97  		if (this.parent != null) {
98  			SpelNodeImpl[] peers = this.parent.children;
99  			for (int i = 0, max = peers.length; i < max; i++) {
100 				if (this == peers[i]) {
101 					if (i + 1 >= max) {
102 						return false;
103 					}
104 					Class<?> clazz = peers[i + 1].getClass();
105 					for (Class<?> desiredClazz : clazzes) {
106 						if (clazz.equals(desiredClazz)) {
107 							return true;
108 						}
109 					}
110 					return false;
111 				}
112 			}
113 		}
114 		return false;
115 	}
116 
117 	@Override
118 	public final Object getValue(ExpressionState expressionState) throws EvaluationException {
119 		if (expressionState != null) {
120 			return getValueInternal(expressionState).getValue();
121 		}
122 		else {
123 			// configuration not set - does that matter?
124 			return getValue(new ExpressionState(new StandardEvaluationContext()));
125 		}
126 	}
127 
128 	@Override
129 	public final TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException {
130 		if (expressionState != null) {
131 			return getValueInternal(expressionState);
132 		}
133 		else {
134 			// configuration not set - does that matter?
135 			return getTypedValue(new ExpressionState(new StandardEvaluationContext()));
136 		}
137 	}
138 
139 	// by default Ast nodes are not writable
140 	@Override
141 	public boolean isWritable(ExpressionState expressionState) throws EvaluationException {
142 		return false;
143 	}
144 
145 	@Override
146 	public void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException {
147 		throw new SpelEvaluationException(getStartPosition(),
148 				SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
149 	}
150 
151 	@Override
152 	public SpelNode getChild(int index) {
153 		return this.children[index];
154 	}
155 
156 	@Override
157 	public int getChildCount() {
158 		return this.children.length;
159 	}
160 
161 	@Override
162 	public Class<?> getObjectClass(Object obj) {
163 		if (obj == null) {
164 			return null;
165 		}
166 		return (obj instanceof Class ? ((Class<?>) obj) : obj.getClass());
167 	}
168 
169 	protected final <T> T getValue(ExpressionState state, Class<T> desiredReturnType) throws EvaluationException {
170 		return ExpressionUtils.convertTypedValue(state.getEvaluationContext(), getValueInternal(state), desiredReturnType);
171 	}
172 
173 	@Override
174 	public int getStartPosition() {
175 		return (this.pos >> 16);
176 	}
177 
178 	@Override
179 	public int getEndPosition() {
180 		return (this.pos & 0xffff);
181 	}
182 
183 	protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
184 		throw new SpelEvaluationException(this.pos, SpelMessage.NOT_ASSIGNABLE, toStringAST());
185 	}
186 
187 	/**
188 	 * Check whether a node can be compiled to bytecode. The reasoning in each node may
189 	 * be different but will typically involve checking whether the exit type descriptor
190 	 * of the node is known and any relevant child nodes are compilable.
191 	 * @return {@code true} if this node can be compiled to bytecode
192 	 */
193 	public boolean isCompilable() {
194 		return false;
195 	}
196 
197 	/**
198 	 * Generate the bytecode for this node into the supplied visitor. Context info about
199 	 * the current expression being compiled is available in the codeflow object. For
200 	 * example it will include information about the type of the object currently
201 	 * on the stack.
202 	 * @param mv the ASM MethodVisitor into which code should be generated
203 	 * @param cf a context object with info about what is on the stack
204 	 */
205 	public void generateCode(MethodVisitor mv, CodeFlow cf) {
206 		throw new IllegalStateException(getClass().getName() +" has no generateCode(..) method");
207 	}
208 
209 	public String getExitDescriptor() {
210 		return this.exitTypeDescriptor;
211 	}
212 
213 	public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException;
214 
215 	
216 	/**
217 	 * Generate code that handles building the argument values for the specified method. This method will take account
218 	 * of whether the invoked method is a varargs method and if it is then the argument values will be appropriately
219 	 * packaged into an array.
220 	 * @param mv the method visitor where code should be generated
221 	 * @param cf the current codeflow
222 	 * @param member the method or constructor for which arguments are being setup
223 	 * @param arguments the expression nodes for the expression supplied argument values
224 	 */
225 	protected static void generateCodeForArguments(MethodVisitor mv, CodeFlow cf, Member member, SpelNodeImpl[] arguments) {
226 		String[] paramDescriptors = null;
227 		boolean isVarargs = false;
228 		if (member instanceof Constructor) {
229 			Constructor<?> ctor = (Constructor<?>)member;
230 			paramDescriptors = CodeFlow.toDescriptors(ctor.getParameterTypes());
231 			isVarargs = ctor.isVarArgs();
232 		}
233 		else { // Method
234 			Method method = (Method)member;
235 			paramDescriptors = CodeFlow.toDescriptors(method.getParameterTypes());
236 			isVarargs = method.isVarArgs();
237 		}
238 		if (isVarargs) {
239 			// The final parameter may or may not need packaging into an array, or nothing may
240 			// have been passed to satisfy the varargs and so something needs to be built.
241 			int p = 0; // Current supplied argument being processed
242 			int childcount = arguments.length;
243 						
244 			// Fulfill all the parameter requirements except the last one
245 			for (p = 0; p < paramDescriptors.length-1;p++) {
246 				generateCodeForArgument(mv, cf, arguments[p], paramDescriptors[p]);
247 			}
248 			
249 			SpelNodeImpl lastchild = (childcount == 0 ? null : arguments[childcount-1]);			
250 			String arraytype = paramDescriptors[paramDescriptors.length-1];
251 			// Determine if the final passed argument is already suitably packaged in array
252 			// form to be passed to the method
253 			if (lastchild != null && lastchild.getExitDescriptor().equals(arraytype)) {
254 				generateCodeForArgument(mv, cf, lastchild, paramDescriptors[p]);
255 			}
256 			else {
257 				arraytype = arraytype.substring(1); // trim the leading '[', may leave other '['		
258 				// build array big enough to hold remaining arguments
259 				CodeFlow.insertNewArrayCode(mv, childcount-p, arraytype);
260 				// Package up the remaining arguments into the array
261 				int arrayindex = 0;
262 				while (p < childcount) {
263 					SpelNodeImpl child = arguments[p];
264 					mv.visitInsn(DUP);
265 					CodeFlow.insertOptimalLoad(mv, arrayindex++);
266 					generateCodeForArgument(mv, cf, child, arraytype);
267 					CodeFlow.insertArrayStore(mv, arraytype);
268 					p++;
269 				}
270 			}
271 		}
272 		else {
273 			for (int i = 0; i < paramDescriptors.length;i++) {
274 				generateCodeForArgument(mv, cf, arguments[i], paramDescriptors[i]);
275 			}
276 		}
277 	}
278 
279 	/**
280 	 * Ask an argument to generate its bytecode and then follow it up
281 	 * with any boxing/unboxing/checkcasting to ensure it matches the expected parameter descriptor.
282 	 */
283 	protected static void generateCodeForArgument(MethodVisitor mv, CodeFlow cf, SpelNodeImpl argument, String paramDescriptor) {
284 		cf.enterCompilationScope();
285 		argument.generateCode(mv, cf);
286 		boolean primitiveOnStack = CodeFlow.isPrimitive(cf.lastDescriptor());
287 		// Check if need to box it for the method reference?
288 		if (primitiveOnStack && paramDescriptor.charAt(0) == 'L') {
289 			CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
290 		}
291 		else if (paramDescriptor.length() == 1 && !primitiveOnStack) {
292 			CodeFlow.insertUnboxInsns(mv, paramDescriptor.charAt(0), cf.lastDescriptor());
293 		}
294 		else if (!cf.lastDescriptor().equals(paramDescriptor)) {
295 			// This would be unnecessary in the case of subtyping (e.g. method takes Number but Integer passed in)
296 			CodeFlow.insertCheckCast(mv, paramDescriptor);
297 		}
298 		cf.exitCompilationScope();
299 	}
300 }