Skip to content

Commit e332e32

Browse files
committed
SpelExpression consistently exposes EvaluationContext to compiled AST
Operator includes explicit support for Boolean comparisons now. Issue: SPR-17229 (cherry picked from commit 51cee65)
1 parent 648fa60 commit e332e32

File tree

2 files changed

+30
-31
lines changed

2 files changed

+30
-31
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -41,7 +41,7 @@
4141
public abstract class Operator extends SpelNodeImpl {
4242

4343
private final String operatorName;
44-
44+
4545
// The descriptors of the runtime operand values are used if the discovered declared
4646
// descriptors are not providing enough information (for example a generic type
4747
// whose accessors seem to only be returning 'Object' - the actual descriptors may
@@ -73,7 +73,8 @@ public final String getOperatorName() {
7373
}
7474

7575
/**
76-
* String format for all operators is the same '(' [operand] [operator] [operand] ')'
76+
* String format for all operators is the same
77+
* {@code '(' [operand] [operator] [operand] ')'}.
7778
*/
7879
@Override
7980
public String toStringAST() {
@@ -103,29 +104,29 @@ protected boolean isCompilableOperatorUsingNumerics() {
103104
return (dc.areNumbers && dc.areCompatible);
104105
}
105106

106-
/**
107-
* Numeric comparison operators share very similar generated code, only differing in
107+
/**
108+
* Numeric comparison operators share very similar generated code, only differing in
108109
* two comparison instructions.
109110
*/
110111
protected void generateComparisonCode(MethodVisitor mv, CodeFlow cf, int compInstruction1, int compInstruction2) {
111112
SpelNodeImpl left = getLeftOperand();
112113
SpelNodeImpl right = getRightOperand();
113114
String leftDesc = left.exitTypeDescriptor;
114115
String rightDesc = right.exitTypeDescriptor;
115-
116+
116117
boolean unboxLeft = !CodeFlow.isPrimitive(leftDesc);
117118
boolean unboxRight = !CodeFlow.isPrimitive(rightDesc);
118119
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(
119120
leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor);
120121
char targetType = dc.compatibleType; // CodeFlow.toPrimitiveTargetDesc(leftDesc);
121-
122+
122123
cf.enterCompilationScope();
123124
left.generateCode(mv, cf);
124125
cf.exitCompilationScope();
125126
if (unboxLeft) {
126127
CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
127128
}
128-
129+
129130
cf.enterCompilationScope();
130131
right.generateCode(mv, cf);
131132
cf.exitCompilationScope();
@@ -141,11 +142,11 @@ protected void generateComparisonCode(MethodVisitor mv, CodeFlow cf, int compIns
141142
mv.visitJumpInsn(compInstruction1, elseTarget);
142143
}
143144
else if (targetType == 'F') {
144-
mv.visitInsn(FCMPG);
145+
mv.visitInsn(FCMPG);
145146
mv.visitJumpInsn(compInstruction1, elseTarget);
146147
}
147148
else if (targetType == 'J') {
148-
mv.visitInsn(LCMP);
149+
mv.visitInsn(LCMP);
149150
mv.visitJumpInsn(compInstruction1, elseTarget);
150151
}
151152
else if (targetType == 'I') {
@@ -217,6 +218,10 @@ else if (leftNumber instanceof Byte || rightNumber instanceof Byte) {
217218
return left.toString().equals(right.toString());
218219
}
219220

221+
if (left instanceof Boolean && right instanceof Boolean) {
222+
return left.equals(right);
223+
}
224+
220225
if (ObjectUtils.nullSafeEquals(left, right)) {
221226
return true;
222227
}
@@ -230,7 +235,7 @@ else if (leftNumber instanceof Byte || rightNumber instanceof Byte) {
230235

231236
return false;
232237
}
233-
238+
234239

235240
/**
236241
* A descriptor comparison encapsulates the result of comparing descriptor
@@ -253,7 +258,7 @@ private DescriptorComparison(boolean areNumbers, boolean areCompatible, char com
253258
this.areCompatible = areCompatible;
254259
this.compatibleType = compatibleType;
255260
}
256-
261+
257262
/**
258263
* Return an object that indicates whether the input descriptors are compatible.
259264
* <p>A declared descriptor is what could statically be determined (e.g. from looking
@@ -277,7 +282,7 @@ public static DescriptorComparison checkNumericCompatibility(
277282

278283
boolean leftNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(ld);
279284
boolean rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd);
280-
285+
281286
// If the declared descriptors aren't providing the information, try the actual descriptors
282287
if (!leftNumeric && !ObjectUtils.nullSafeEquals(ld, leftActualDescriptor)) {
283288
ld = leftActualDescriptor;
@@ -287,7 +292,7 @@ public static DescriptorComparison checkNumericCompatibility(
287292
rd = rightActualDescriptor;
288293
rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd);
289294
}
290-
295+
291296
if (leftNumeric && rightNumeric) {
292297
if (CodeFlow.areBoxingCompatible(ld, rd)) {
293298
return new DescriptorComparison(true, true, CodeFlow.toPrimitiveTargetDesc(ld));
@@ -298,7 +303,7 @@ public static DescriptorComparison checkNumericCompatibility(
298303
}
299304
else {
300305
return DescriptorComparison.NOT_NUMBERS;
301-
}
306+
}
302307
}
303308
}
304309

spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpression.java

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -118,10 +118,8 @@ public String getExpressionString() {
118118
public Object getValue() throws EvaluationException {
119119
if (this.compiledAst != null) {
120120
try {
121-
TypedValue contextRoot =
122-
(this.evaluationContext != null ? this.evaluationContext.getRootObject() : null);
123-
return this.compiledAst.getValue(
124-
(contextRoot != null ? contextRoot.getValue() : null), this.evaluationContext);
121+
EvaluationContext context = getEvaluationContext();
122+
return this.compiledAst.getValue(context.getRootObject().getValue(), context);
125123
}
126124
catch (Throwable ex) {
127125
// If running in mixed mode, revert to interpreted
@@ -148,10 +146,8 @@ public Object getValue() throws EvaluationException {
148146
public <T> T getValue(@Nullable Class<T> expectedResultType) throws EvaluationException {
149147
if (this.compiledAst != null) {
150148
try {
151-
TypedValue contextRoot =
152-
(this.evaluationContext != null ? this.evaluationContext.getRootObject() : null);
153-
Object result = this.compiledAst.getValue(
154-
(contextRoot != null ? contextRoot.getValue() : null), this.evaluationContext);
149+
EvaluationContext context = getEvaluationContext();
150+
Object result = this.compiledAst.getValue(context.getRootObject().getValue(), context);
155151
if (expectedResultType == null) {
156152
return (T) result;
157153
}
@@ -185,7 +181,7 @@ public <T> T getValue(@Nullable Class<T> expectedResultType) throws EvaluationEx
185181
public Object getValue(Object rootObject) throws EvaluationException {
186182
if (this.compiledAst != null) {
187183
try {
188-
return this.compiledAst.getValue(rootObject, evaluationContext);
184+
return this.compiledAst.getValue(rootObject, getEvaluationContext());
189185
}
190186
catch (Throwable ex) {
191187
// If running in mixed mode, revert to interpreted
@@ -213,7 +209,7 @@ public Object getValue(Object rootObject) throws EvaluationException {
213209
public <T> T getValue(Object rootObject, @Nullable Class<T> expectedResultType) throws EvaluationException {
214210
if (this.compiledAst != null) {
215211
try {
216-
Object result = this.compiledAst.getValue(rootObject, null);
212+
Object result = this.compiledAst.getValue(rootObject, getEvaluationContext());
217213
if (expectedResultType == null) {
218214
return (T)result;
219215
}
@@ -250,8 +246,7 @@ public Object getValue(EvaluationContext context) throws EvaluationException {
250246

251247
if (this.compiledAst != null) {
252248
try {
253-
TypedValue contextRoot = context.getRootObject();
254-
return this.compiledAst.getValue(contextRoot.getValue(), context);
249+
return this.compiledAst.getValue(context.getRootObject().getValue(), context);
255250
}
256251
catch (Throwable ex) {
257252
// If running in mixed mode, revert to interpreted
@@ -280,8 +275,7 @@ public <T> T getValue(EvaluationContext context, @Nullable Class<T> expectedResu
280275

281276
if (this.compiledAst != null) {
282277
try {
283-
TypedValue contextRoot = context.getRootObject();
284-
Object result = this.compiledAst.getValue(contextRoot.getValue(), context);
278+
Object result = this.compiledAst.getValue(context.getRootObject().getValue(), context);
285279
if (expectedResultType != null) {
286280
return ExpressionUtils.convertTypedValue(context, new TypedValue(result), expectedResultType);
287281
}
@@ -315,7 +309,7 @@ public Object getValue(EvaluationContext context, Object rootObject) throws Eval
315309

316310
if (this.compiledAst != null) {
317311
try {
318-
return this.compiledAst.getValue(rootObject,context);
312+
return this.compiledAst.getValue(rootObject, context);
319313
}
320314
catch (Throwable ex) {
321315
// If running in mixed mode, revert to interpreted

0 commit comments

Comments
 (0)