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.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
32
33
34
35
36
37
38
39
40 public abstract class Operator extends SpelNodeImpl {
41
42 private final String operatorName;
43
44
45
46
47
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
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
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
104
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;
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
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
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
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
219
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;
228
229 final boolean areCompatible;
230
231 final char compatibleType;
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
241
242
243
244
245
246
247
248
249
250
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
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 }