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.math.BigDecimal;
20  import java.math.BigInteger;
21  
22  import org.springframework.asm.Label;
23  import org.springframework.asm.MethodVisitor;
24  import org.springframework.expression.spel.CodeFlow;
25  import org.springframework.expression.spel.ExpressionState;
26  import org.springframework.util.ClassUtils;
27  import org.springframework.util.NumberUtils;
28  import org.springframework.util.ObjectUtils;
29  
30  /**
31   * Common supertype for operators that operate on either one or two operands.
32   * In the case of multiply or divide there would be two operands, but for
33   * unary plus or minus, there is only one.
34   *
35   * @author Andy Clement
36   * @author Juergen Hoeller
37   * @author Giovanni Dall'Oglio Risso
38   * @since 3.0
39   */
40  public abstract class Operator extends SpelNodeImpl {
41  
42  	private final String operatorName;
43  	
44  	// The descriptors of the runtime operand values are used if the discovered declared
45  	// descriptors are not providing enough information (for example a generic type
46  	// whose accessors seem to only be returning 'Object' - the actual descriptors may
47  	// indicate 'int')
48  
49  	protected String leftActualDescriptor;
50  
51  	protected String rightActualDescriptor;
52  
53  
54  	public Operator(String payload,int pos,SpelNodeImpl... operands) {
55  		super(pos, operands);
56  		this.operatorName = payload;
57  	}
58  
59  
60  	public SpelNodeImpl getLeftOperand() {
61  		return this.children[0];
62  	}
63  
64  	public SpelNodeImpl getRightOperand() {
65  		return this.children[1];
66  	}
67  
68  	public final String getOperatorName() {
69  		return this.operatorName;
70  	}
71  
72  	/**
73  	 * String format for all operators is the same '(' [operand] [operator] [operand] ')'
74  	 */
75  	@Override
76  	public String toStringAST() {
77  		StringBuilder sb = new StringBuilder("(");
78  		sb.append(getChild(0).toStringAST());
79  		for (int i = 1; i < getChildCount(); i++) {
80  			sb.append(" ").append(getOperatorName()).append(" ");
81  			sb.append(getChild(i).toStringAST());
82  		}
83  		sb.append(")");
84  		return sb.toString();
85  	}
86  
87  	protected boolean isCompilableOperatorUsingNumerics() {
88  		SpelNodeImpl left = getLeftOperand();
89  		SpelNodeImpl right= getRightOperand();
90  		if (!left.isCompilable() || !right.isCompilable()) {
91  			return false;
92  		}
93  
94  		// Supported operand types for equals (at the moment)
95  		String leftDesc = left.exitTypeDescriptor;
96  		String rightDesc = right.exitTypeDescriptor;
97  		DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc,
98  				this.leftActualDescriptor, this.rightActualDescriptor);
99  		return (dc.areNumbers && dc.areCompatible);
100 	}
101 
102 	/** 
103 	 * Numeric comparison operators share very similar generated code, only differing in 
104 	 * two comparison instructions.
105 	 */
106 	protected void generateComparisonCode(MethodVisitor mv, CodeFlow cf, int compInstruction1, int compInstruction2) {
107 		String leftDesc = getLeftOperand().exitTypeDescriptor;
108 		String rightDesc = getRightOperand().exitTypeDescriptor;
109 		
110 		boolean unboxLeft = !CodeFlow.isPrimitive(leftDesc);
111 		boolean unboxRight = !CodeFlow.isPrimitive(rightDesc);
112 		DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc,
113 				this.leftActualDescriptor, this.rightActualDescriptor);
114 		char targetType = dc.compatibleType;//CodeFlow.toPrimitiveTargetDesc(leftDesc);
115 		
116 		getLeftOperand().generateCode(mv, cf);
117 		if (unboxLeft) {
118 			CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
119 		}
120 	
121 		cf.enterCompilationScope();
122 		getRightOperand().generateCode(mv, cf);
123 		cf.exitCompilationScope();
124 		if (unboxRight) {
125 			CodeFlow.insertUnboxInsns(mv, targetType, rightDesc);
126 		}
127 
128 		// assert: SpelCompiler.boxingCompatible(leftDesc, rightDesc)
129 		Label elseTarget = new Label();
130 		Label endOfIf = new Label();
131 		if (targetType=='D') {
132 			mv.visitInsn(DCMPG);
133 			mv.visitJumpInsn(compInstruction1, elseTarget);
134 		}
135 		else if (targetType=='F') {
136 			mv.visitInsn(FCMPG);		
137 			mv.visitJumpInsn(compInstruction1, elseTarget);
138 		}
139 		else if (targetType=='J') {
140 			mv.visitInsn(LCMP);		
141 			mv.visitJumpInsn(compInstruction1, elseTarget);
142 		}
143 		else if (targetType=='I') {
144 			mv.visitJumpInsn(compInstruction2, elseTarget);
145 		}
146 		else {
147 			throw new IllegalStateException("Unexpected descriptor "+leftDesc);
148 		}
149 
150 		// Other numbers are not yet supported (isCompilable will not have returned true)
151 		mv.visitInsn(ICONST_1);
152 		mv.visitJumpInsn(GOTO,endOfIf);
153 		mv.visitLabel(elseTarget);
154 		mv.visitInsn(ICONST_0);
155 		mv.visitLabel(endOfIf);
156 		cf.pushDescriptor("Z");
157 	}
158 
159 	protected boolean equalityCheck(ExpressionState state, Object left, Object right) {
160 		if (left instanceof Number && right instanceof Number) {
161 			Number leftNumber = (Number) left;
162 			Number rightNumber = (Number) right;
163 
164 			if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
165 				BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
166 				BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
167 				return (leftBigDecimal == null ? rightBigDecimal == null : leftBigDecimal.compareTo(rightBigDecimal) == 0);
168 			}
169 			else if (leftNumber instanceof Double || rightNumber instanceof Double) {
170 				return (leftNumber.doubleValue() == rightNumber.doubleValue());
171 			}
172 			else if (leftNumber instanceof Float || rightNumber instanceof Float) {
173 				return (leftNumber.floatValue() == rightNumber.floatValue());
174 			}
175 			else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
176 				BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
177 				BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
178 				return (leftBigInteger == null ? rightBigInteger == null : leftBigInteger.compareTo(rightBigInteger) == 0);
179 			}
180 			else if (leftNumber instanceof Long || rightNumber instanceof Long) {
181 				return (leftNumber.longValue() == rightNumber.longValue());
182 			}
183 			else if (leftNumber instanceof Integer || rightNumber instanceof Integer) {
184 				return (leftNumber.intValue() == rightNumber.intValue());
185 			}
186 			else if (leftNumber instanceof Short || rightNumber instanceof Short) {
187 				return (leftNumber.shortValue() == rightNumber.shortValue());
188 			}
189 			else if (leftNumber instanceof Byte || rightNumber instanceof Byte) {
190 				return (leftNumber.byteValue() == rightNumber.byteValue());
191 			}
192 			else {
193 				// Unknown Number subtypes -> best guess is double comparison
194 				return (leftNumber.doubleValue() == rightNumber.doubleValue());
195 			}
196 		}
197 
198 		if (left instanceof CharSequence && right instanceof CharSequence) {
199 			return left.toString().equals(right.toString());
200 		}
201 
202 		if (ObjectUtils.nullSafeEquals(left, right)) {
203 			return true;
204 		}
205 
206 		if (left instanceof Comparable && right instanceof Comparable) {
207 			Class<?> ancestor = ClassUtils.determineCommonAncestor(left.getClass(), right.getClass());
208 			if (ancestor != null && Comparable.class.isAssignableFrom(ancestor)) {
209 				return (state.getTypeComparator().compare(left, right) == 0);
210 			}
211 		}
212 
213 		return false;
214 	}
215 	
216 
217 	/**
218 	 * A descriptor comparison encapsulates the result of comparing descriptor for two operands and
219 	 * describes at what level they are compatible.
220 	 */
221 	protected static class DescriptorComparison {
222 
223 		static DescriptorComparison NOT_NUMBERS = new DescriptorComparison(false, false, ' ');
224 
225 		static DescriptorComparison INCOMPATIBLE_NUMBERS = new DescriptorComparison(true, false, ' ');
226 
227 		final boolean areNumbers; // Were the two compared descriptor both for numbers?
228 
229 		final boolean areCompatible; // If they were numbers, were they compatible?
230 
231 		final char compatibleType; // When compatible, what is the descriptor of the common type
232 		
233 		private DescriptorComparison(boolean areNumbers, boolean areCompatible, char compatibleType) {
234 			this.areNumbers = areNumbers;
235 			this.areCompatible = areCompatible;
236 			this.compatibleType = compatibleType;
237 		}
238 		
239 		/**
240 		 * Returns an object that indicates whether the input descriptors are compatible. A declared descriptor
241 		 * is what could statically be determined (e.g. from looking at the return value of a property accessor
242 		 * method) whilst an actual descriptor is the type of an actual object that was returned, which may differ.
243 		 * For generic types with unbound type variables the declared descriptor discovered may be 'Object' but
244 		 * from the actual descriptor it is possible to observe that the objects are really numeric values (e.g.
245 		 * ints).
246 		 * @param leftDeclaredDescriptor the statically determinable left descriptor
247 		 * @param rightDeclaredDescriptor the statically determinable right descriptor
248 		 * @param leftActualDescriptor the dynamic/runtime left object descriptor
249 		 * @param rightActualDescriptor the dynamic/runtime right object descriptor
250 		 * @return a DescriptorComparison object indicating the type of compatibility, if any
251 		 */
252 		public static DescriptorComparison checkNumericCompatibility(String leftDeclaredDescriptor,
253 				String rightDeclaredDescriptor, String leftActualDescriptor, String rightActualDescriptor) {
254 
255 			String ld = leftDeclaredDescriptor;
256 			String rd = rightDeclaredDescriptor;
257 
258 			boolean leftNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(ld);
259 			boolean rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd);
260 			
261 			// If the declared descriptors aren't providing the information, try the actual descriptors
262 			if (!leftNumeric && !ld.equals(leftActualDescriptor)) {
263 				ld = leftActualDescriptor;
264 				leftNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(ld);
265 			}
266 			if (!rightNumeric && !rd.equals(rightActualDescriptor)) {
267 				rd = rightActualDescriptor;
268 				rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd);
269 			}
270 			
271 			if (leftNumeric && rightNumeric) {
272 				if (CodeFlow.areBoxingCompatible(ld, rd)) {
273 					return new DescriptorComparison(true, true, CodeFlow.toPrimitiveTargetDesc(ld));
274 				}
275 				else {
276 					return DescriptorComparison.INCOMPATIBLE_NUMBERS;
277 				}
278 			}
279 			else {
280 				return DescriptorComparison.NOT_NUMBERS;
281 			}		
282 		}
283 	}
284 
285 }