Rework compilation of OpNE/OpEQ SpEL operators

For SPR-14863 we need to adjust the code generation for OpNE
to use !x.equals(y) rather than x!=y. There are also further
cases in the equalityCheck() code in Operator that were not
being handled in the compilation case (when comparators are
used for example). This latter issue also affects OpEQ.

Rather than add yet more bytecode generation, both OpNE and
OpEQ generateCode() methods have been simplified. The
generated code now delegates to equalityCheck() in Operator
which is exactly what the interpreted case does.

This ensures that the compiled code continues to behave just
like the interpreted case. It ensures changes to the interpreted
case are automatically picked up for the compiled case. It
makes the bytecode generation simpler.

The benefit of compilation of SpEL expressions is to avoid
slow reflective calls - that doesn't apply for a basic
(in)equality test so there is no need to go crazy in bytecode
gen.

Issue: SPR-14863
(cherry picked from commit 9000acd)
This commit is contained in:
Andy Clement 2016-11-01 21:42:23 +01:00 committed by Juergen Hoeller
parent 77e00f1926
commit 38cd1ecff4
5 changed files with 1425 additions and 1312 deletions

View File

@ -28,15 +28,28 @@ import org.springframework.asm.Opcodes;
import org.springframework.util.Assert;
/**
* Manages the class being generated by the compilation process. It records
* intermediate compilation state as the bytecode is generated. It also includes
* various bytecode generation helper functions.
* Manages the class being generated by the compilation process.
*
* <p>Records intermediate compilation state as the bytecode is generated.
* Also includes various bytecode generation helper functions.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 4.1
*/
public class CodeFlow implements Opcodes {
/**
* Name of the class being generated. Typically used when generating code
* that accesses freshly generated fields on the generated type.
*/
private final String className;
/**
* The current class being generated.
*/
private final ClassWriter classWriter;
/**
* Record the type of what is on top of the bytecode stack (i.e. the type of the
* output from the previous expression component). New scopes are used to evaluate
@ -45,17 +58,12 @@ public class CodeFlow implements Opcodes {
*/
private final Stack<ArrayList<String>> compilationScopes;
/**
* The current class being generated
*/
private ClassWriter cw;
/**
* As SpEL ast nodes are called to generate code for the main evaluation method
* they can register to add a field to this class. Any registered FieldAdders
* will be called after the main evaluation function has finished being generated.
*/
private List<FieldAdder> fieldAdders = null;
private List<FieldAdder> fieldAdders;
/**
* As SpEL ast nodes are called to generate code for the main evaluation method
@ -63,13 +71,7 @@ public class CodeFlow implements Opcodes {
* registered ClinitAdders will be called after the main evaluation function
* has finished being generated.
*/
private List<ClinitAdder> clinitAdders = null;
/**
* Name of the class being generated. Typically used when generating code
* that accesses freshly generated fields on the generated type.
*/
private String clazzName;
private List<ClinitAdder> clinitAdders;
/**
* When code generation requires holding a value in a class level field, this
@ -83,13 +85,20 @@ public class CodeFlow implements Opcodes {
*/
private int nextFreeVariableId = 1;
public CodeFlow(String clazzName, ClassWriter cw) {
/**
* Construct a new {@code CodeFlow} for the given class.
* @param className the name of the class
* @param classWriter the corresponding ASM {@code ClassWriter}
*/
public CodeFlow(String className, ClassWriter classWriter) {
this.className = className;
this.classWriter = classWriter;
this.compilationScopes = new Stack<ArrayList<String>>();
this.compilationScopes.add(new ArrayList<String>());
this.cw = cw;
this.clazzName = clazzName;
}
/**
* Push the byte code to load the target (i.e. what was passed as the first argument
* to CompiledExpression.getValue(target, context))
@ -99,6 +108,16 @@ public class CodeFlow implements Opcodes {
mv.visitVarInsn(ALOAD, 1);
}
/**
* Push the bytecode to load the EvaluationContext (the second parameter passed to
* the compiled expression method).
* @param mv the visitor into which the load instruction should be inserted
* @since 4.3.4
*/
public void loadEvaluationContext(MethodVisitor mv) {
mv.visitVarInsn(ALOAD, 2);
}
/**
* Record the descriptor for the most recently evaluated expression element.
* @param descriptor type descriptor for most recently evaluated element
@ -155,13 +174,13 @@ public class CodeFlow implements Opcodes {
public void finish() {
if (this.fieldAdders != null) {
for (FieldAdder fieldAdder : this.fieldAdders) {
fieldAdder.generateField(cw,this);
fieldAdder.generateField(this.classWriter, this);
}
}
if (this.clinitAdders != null) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", "()V", null, null);
MethodVisitor mv = this.classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
this.nextFreeVariableId = 0; // To 0 because there is no 'this' in a clinit
this.nextFreeVariableId = 0; // to 0 because there is no 'this' in a clinit
for (ClinitAdder clinitAdder : this.clinitAdders) {
clinitAdder.generateCode(mv, this);
}
@ -204,18 +223,18 @@ public class CodeFlow implements Opcodes {
}
public String getClassName() {
return this.clazzName;
return this.className;
}
@Deprecated
public String getClassname() {
return this.clazzName;
return this.className;
}
/**
* Insert any necessary cast and value call to convert from a boxed type to a
* primitive value
* primitive value.
* @param mv the method visitor into which instructions should be inserted
* @param ch the primitive type desired as output
* @param stackDescriptor the descriptor of the type on top of the stack

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +16,8 @@
package org.springframework.expression.spel.ast;
import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
@ -43,7 +43,8 @@ public class OpEQ extends Operator {
Object right = getRightOperand().getValueInternal(state).getValue();
this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
return BooleanTypedValue.forValue(equalityCheck(state, left, right));
return BooleanTypedValue.forValue(
equalityCheck(state.getEvaluationContext(), left, right));
}
// This check is different to the one in the other numeric operators (OpLt/etc)
@ -58,90 +59,36 @@ public class OpEQ extends Operator {
String leftDesc = left.exitTypeDescriptor;
String rightDesc = right.exitTypeDescriptor;
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(
leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor);
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc,
rightDesc, this.leftActualDescriptor, this.rightActualDescriptor);
return (!dc.areNumbers || dc.areCompatible);
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow cf) {
cf.loadEvaluationContext(mv);
String leftDesc = getLeftOperand().exitTypeDescriptor;
String rightDesc = getRightOperand().exitTypeDescriptor;
Label elseTarget = new Label();
Label endOfIf = new Label();
boolean leftPrim = CodeFlow.isPrimitive(leftDesc);
boolean rightPrim = CodeFlow.isPrimitive(rightDesc);
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(
leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor);
if (dc.areNumbers && dc.areCompatible) {
char targetType = dc.compatibleType;
cf.enterCompilationScope();
getLeftOperand().generateCode(mv, cf);
if (!leftPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
cf.exitCompilationScope();
if (leftPrim) {
CodeFlow.insertBoxIfNecessary(mv, leftDesc.charAt(0));
}
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
cf.exitCompilationScope();
if (!rightPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, rightDesc);
}
// assert: SpelCompiler.boxingCompatible(leftDesc, rightDesc)
if (targetType == 'D') {
mv.visitInsn(DCMPL);
mv.visitJumpInsn(IFNE, elseTarget);
}
else if (targetType == 'F') {
mv.visitInsn(FCMPL);
mv.visitJumpInsn(IFNE, elseTarget);
}
else if (targetType == 'J') {
mv.visitInsn(LCMP);
mv.visitJumpInsn(IFNE, elseTarget);
}
else if (targetType == 'I' || targetType == 'Z') {
mv.visitJumpInsn(IF_ICMPNE, elseTarget);
}
else {
throw new IllegalStateException("Unexpected descriptor " + leftDesc);
}
}
else {
getLeftOperand().generateCode(mv, cf);
if (leftPrim) {
CodeFlow.insertBoxIfNecessary(mv, leftDesc.charAt(0));
}
getRightOperand().generateCode(mv, cf);
if (rightPrim) {
CodeFlow.insertBoxIfNecessary(mv, rightDesc.charAt(0));
}
Label leftNotNull = new Label();
mv.visitInsn(DUP_X1); // dup right on the top of the stack
mv.visitJumpInsn(IFNONNULL, leftNotNull);
// Right is null!
mv.visitInsn(SWAP);
mv.visitInsn(POP); // remove it
Label rightNotNull = new Label();
mv.visitJumpInsn(IFNONNULL, rightNotNull);
// Left is null too
mv.visitInsn(ICONST_1);
mv.visitJumpInsn(GOTO, endOfIf);
mv.visitLabel(rightNotNull);
mv.visitInsn(ICONST_0);
mv.visitJumpInsn(GOTO, endOfIf);
mv.visitLabel(leftNotNull);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false);
mv.visitLabel(endOfIf);
cf.pushDescriptor("Z");
return;
}
mv.visitInsn(ICONST_1);
mv.visitJumpInsn(GOTO, endOfIf);
mv.visitLabel(elseTarget);
mv.visitInsn(ICONST_0);
mv.visitLabel(endOfIf);
String operatorClassName = Operator.class.getName().replace('.', '/');
String evaluationContextClassName = EvaluationContext.class.getName().replace('.', '/');
mv.visitMethodInsn(INVOKESTATIC, operatorClassName, "equalityCheck",
"(L" + evaluationContextClassName + ";Ljava/lang/Object;Ljava/lang/Object;)Z", false);
cf.pushDescriptor("Z");
}

View File

@ -18,6 +18,7 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
@ -43,7 +44,8 @@ public class OpNE extends Operator {
Object right = getRightOperand().getValueInternal(state).getValue();
this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
return BooleanTypedValue.forValue(!equalityCheck(state, left, right));
return BooleanTypedValue.forValue(
!equalityCheck(state.getEvaluationContext(), left, right));
}
// This check is different to the one in the other numeric operators (OpLt/etc)
@ -58,65 +60,47 @@ public class OpNE extends Operator {
String leftDesc = left.exitTypeDescriptor;
String rightDesc = right.exitTypeDescriptor;
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(
leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor);
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc,
rightDesc, this.leftActualDescriptor, this.rightActualDescriptor);
return (!dc.areNumbers || dc.areCompatible);
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow cf) {
cf.loadEvaluationContext(mv);
String leftDesc = getLeftOperand().exitTypeDescriptor;
String rightDesc = getRightOperand().exitTypeDescriptor;
Label elseTarget = new Label();
Label endOfIf = new Label();
boolean leftPrim = CodeFlow.isPrimitive(leftDesc);
boolean rightPrim = CodeFlow.isPrimitive(rightDesc);
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(
leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor);
if (dc.areNumbers && dc.areCompatible) {
char targetType = dc.compatibleType;
cf.enterCompilationScope();
getLeftOperand().generateCode(mv, cf);
if (!leftPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
cf.exitCompilationScope();
if (leftPrim) {
CodeFlow.insertBoxIfNecessary(mv, leftDesc.charAt(0));
}
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
cf.exitCompilationScope();
if (!rightPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, rightDesc);
}
// assert: SpelCompiler.boxingCompatible(leftDesc, rightDesc)
if (targetType == 'D') {
mv.visitInsn(DCMPL);
mv.visitJumpInsn(IFEQ, elseTarget);
}
else if (targetType == 'F') {
mv.visitInsn(FCMPL);
mv.visitJumpInsn(IFEQ, elseTarget);
}
else if (targetType == 'J') {
mv.visitInsn(LCMP);
mv.visitJumpInsn(IFEQ, elseTarget);
}
else if (targetType == 'I' || targetType == 'Z') {
mv.visitJumpInsn(IF_ICMPEQ, elseTarget);
}
else {
throw new IllegalStateException("Unexpected descriptor " + leftDesc);
}
}
else {
getLeftOperand().generateCode(mv, cf);
getRightOperand().generateCode(mv, cf);
mv.visitJumpInsn(IF_ACMPEQ, elseTarget);
if (rightPrim) {
CodeFlow.insertBoxIfNecessary(mv, rightDesc.charAt(0));
}
String operatorClassName = Operator.class.getName().replace('.', '/');
String evaluationContextClassName = EvaluationContext.class.getName().replace('.', '/');
mv.visitMethodInsn(INVOKESTATIC, operatorClassName, "equalityCheck",
"(L" + evaluationContextClassName + ";Ljava/lang/Object;Ljava/lang/Object;)Z", false);
// Invert the boolean
Label notZero = new Label();
Label end = new Label();
mv.visitJumpInsn(IFNE, notZero);
mv.visitInsn(ICONST_1);
mv.visitJumpInsn(GOTO,endOfIf);
mv.visitLabel(elseTarget);
mv.visitJumpInsn(GOTO, end);
mv.visitLabel(notZero);
mv.visitInsn(ICONST_0);
mv.visitLabel(endOfIf);
mv.visitLabel(end);
cf.pushDescriptor("Z");
}

View File

@ -21,8 +21,8 @@ import java.math.BigInteger;
import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.ClassUtils;
import org.springframework.util.NumberUtils;
import org.springframework.util.ObjectUtils;
@ -114,7 +114,9 @@ public abstract class Operator extends SpelNodeImpl {
leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor);
char targetType = dc.compatibleType; // CodeFlow.toPrimitiveTargetDesc(leftDesc);
cf.enterCompilationScope();
getLeftOperand().generateCode(mv, cf);
cf.exitCompilationScope();
if (unboxLeft) {
CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
}
@ -157,7 +159,17 @@ public abstract class Operator extends SpelNodeImpl {
cf.pushDescriptor("Z");
}
protected boolean equalityCheck(ExpressionState state, Object left, Object right) {
/**
* Perform an equality check for the given operand values.
* <p>This method is not just used for reflective comparisons in subclasses
* but also from compiled expression code, which is why it needs to be
* declared as {@code public static} here.
* @param context the current evaluation context
* @param left the left-hand operand value
* @param right the right-hand operand value
*/
public static boolean equalityCheck(EvaluationContext context, Object left, Object right) {
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;
@ -207,7 +219,7 @@ public abstract class Operator extends SpelNodeImpl {
if (left instanceof Comparable && right instanceof Comparable) {
Class<?> ancestor = ClassUtils.determineCommonAncestor(left.getClass(), right.getClass());
if (ancestor != null && Comparable.class.isAssignableFrom(ancestor)) {
return (state.getTypeComparator().compare(left, right) == 0);
return (context.getTypeComparator().compare(left, right) == 0);
}
}

View File

@ -44,16 +44,14 @@ import org.springframework.expression.spel.testdata.PersonInOtherPackage;
import static org.junit.Assert.*;
/**
* Checks the behaviour of the SpelCompiler. This should cover compilation all compiled node types.
* Checks the behaviour of the SpelCompiler.
* This should cover compilation all compiled node types.
*
* @author Andy Clement
* @since 4.1
*/
public class SpelCompilationCoverageTests extends AbstractExpressionTests {
private Expression expression;
private SpelNodeImpl ast;
/*
* Further TODOs for compilation:
*
@ -118,6 +116,12 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
* Selection
*/
private Expression expression;
private SpelNodeImpl ast;
@Test
public void typeReference() throws Exception {
expression = parse("T(String)");
@ -198,7 +202,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertCanCompile(expression);
assertEquals(false, expression.getValue());
List<String> list = new ArrayList<String>();
List<String> list = new ArrayList<>();
expression = parse("#root instanceof T(java.util.List)");
assertEquals(true, expression.getValue(list));
assertCanCompile(expression);
@ -761,8 +765,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
expression = parser.parseExpression("#doFormat('hey %s', 'there')");
context = new StandardEvaluationContext();
context.registerFunction("doFormat",
DelegatingStringFormat.class.getDeclaredMethod("format", String.class,
Object[].class));
DelegatingStringFormat.class.getDeclaredMethod("format", String.class, Object[].class));
((SpelExpression) expression).setEvaluationContext(context);
assertEquals("hey there", expression.getValue(String.class));
@ -773,8 +776,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
expression = parser.parseExpression("#doFormat([0], 'there')");
context = new StandardEvaluationContext(new Object[] {"hey %s"});
context.registerFunction("doFormat",
DelegatingStringFormat.class.getDeclaredMethod("format", String.class,
Object[].class));
DelegatingStringFormat.class.getDeclaredMethod("format", String.class, Object[].class));
((SpelExpression) expression).setEvaluationContext(context);
assertEquals("hey there", expression.getValue(String.class));
@ -785,8 +787,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
expression = parser.parseExpression("#doFormat([0], #arg)");
context = new StandardEvaluationContext(new Object[] {"hey %s"});
context.registerFunction("doFormat",
DelegatingStringFormat.class.getDeclaredMethod("format", String.class,
Object[].class));
DelegatingStringFormat.class.getDeclaredMethod("format", String.class, Object[].class));
context.setVariable("arg", "there");
((SpelExpression) expression).setEvaluationContext(context);
@ -799,7 +800,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
public void functionReference() throws Exception {
EvaluationContext ctx = new StandardEvaluationContext();
Method m = this.getClass().getDeclaredMethod("concat",String.class,String.class);
Method m = getClass().getDeclaredMethod("concat", String.class, String.class);
ctx.setVariable("concat",m);
expression = parser.parseExpression("#concat('a','b')");
@ -1066,7 +1067,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
public void functionReferenceVarargs() throws Exception {
EvaluationContext ctx = new StandardEvaluationContext();
Method m = this.getClass().getDeclaredMethod("join", String[].class);
Method m = getClass().getDeclaredMethod("join", String[].class);
ctx.setVariable("join", m);
expression = parser.parseExpression("#join('a','b','c')");
assertEquals("abc", expression.getValue(ctx));
@ -1335,8 +1336,6 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
public void opEq() throws Exception {
String tvar = "35";
expression = parse("#root == 35");
Boolean bb = (Boolean)expression.getValue(tvar);
System.out.println(bb);
assertFalse((Boolean) expression.getValue(tvar));
assertCanCompile(expression);
assertFalse((Boolean) expression.getValue(tvar));
@ -1623,6 +1622,82 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertTrue((Boolean) expression.getValue());
}
@Test
public void opNe_SPR14863() throws Exception {
SpelParserConfiguration configuration =
new SpelParserConfiguration(SpelCompilerMode.MIXED, ClassLoader.getSystemClassLoader());
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression expression = parser.parseExpression("data['my-key'] != 'my-value'");
Map<String, String> data = new HashMap<>();
data.put("my-key", new String("my-value"));
StandardEvaluationContext context = new StandardEvaluationContext(new MyContext(data));
assertFalse(expression.getValue(context, Boolean.class));
assertCanCompile(expression);
((SpelExpression) expression).compileExpression();
assertFalse(expression.getValue(context, Boolean.class));
List<String> ls = new ArrayList<String>();
ls.add(new String("foo"));
context = new StandardEvaluationContext(ls);
expression = parse("get(0) != 'foo'");
assertFalse(expression.getValue(context, Boolean.class));
assertCanCompile(expression);
assertFalse(expression.getValue(context, Boolean.class));
ls.remove(0);
ls.add("goo");
assertTrue(expression.getValue(context, Boolean.class));
}
@Test
public void opEq_SPR14863() throws Exception {
// Exercise the comparator invocation code that runs in
// equalityCheck() (called from interpreted and compiled code)
expression = parser.parseExpression("#aa==#bb");
StandardEvaluationContext sec = new StandardEvaluationContext();
Apple aa = new Apple(1);
Apple bb = new Apple(2);
sec.setVariable("aa",aa);
sec.setVariable("bb",bb);
boolean b = expression.getValue(sec, Boolean.class);
// Verify what the expression caused aa to be compared to
assertEquals(bb,aa.gotComparedTo);
assertFalse(b);
bb.setValue(1);
b = expression.getValue(sec, Boolean.class);
assertEquals(bb,aa.gotComparedTo);
assertTrue(b);
assertCanCompile(expression);
// Similar test with compiled expression
aa = new Apple(99);
bb = new Apple(100);
sec.setVariable("aa",aa);
sec.setVariable("bb",bb);
b = expression.getValue(sec, Boolean.class);
assertFalse(b);
assertEquals(bb,aa.gotComparedTo);
bb.setValue(99);
b = expression.getValue(sec, Boolean.class);
assertTrue(b);
assertEquals(bb,aa.gotComparedTo);
List<String> ls = new ArrayList<String>();
ls.add(new String("foo"));
StandardEvaluationContext context = new StandardEvaluationContext(ls);
expression = parse("get(0) == 'foo'");
assertTrue(expression.getValue(context, Boolean.class));
assertCanCompile(expression);
assertTrue(expression.getValue(context, Boolean.class));
ls.remove(0);
ls.add("goo");
assertFalse(expression.getValue(context, Boolean.class));
}
@Test
public void opPlus() throws Exception {
expression = parse("2+2");
@ -2616,7 +2691,6 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
checkCalc(p,"payload.valueI*payload.valueBB20",2400);
}
@Test
public void opModulus_mixedNumberTypes() throws Exception {
PayloadX p = new PayloadX();
@ -2923,11 +2997,11 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertEquals(0, expression.getValue());
expression = parse("payload%2==0");
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(5),Boolean.TYPE));
assertTrue(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper<>(5), Boolean.TYPE));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(5),Boolean.TYPE));
assertTrue(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper<>(5), Boolean.TYPE));
expression = parse("8%3");
assertEquals(2, expression.getValue());
@ -3104,7 +3178,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
public void constructorReference_SPR12326() {
String type = this.getClass().getName();
String type = getClass().getName();
String prefix = "new " + type + ".Obj";
expression = parser.parseExpression(prefix + "([0])");
@ -3183,7 +3257,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
// Variant of above more like what was in the bug report:
SpelExpressionParser parser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.getClass().getClassLoader()));
getClass().getClassLoader()));
SpelExpression ex = parser.parseRaw("#it?.age.equals([0])");
context = new StandardEvaluationContext(new Object[] { person.getAge() });
@ -3783,7 +3857,6 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertTrue(see.getCause() instanceof ClassCastException);
}
// method with changing target
expression = parser.parseExpression("#root.charAt(0)");
assertEquals('a', expression.getValue("abc"));
@ -3897,11 +3970,11 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
public void mixingItUp_indexerOpEqTernary() throws Exception {
Map<String, String> m = new HashMap<String,String>();
Map<String, String> m = new HashMap<>();
m.put("andy","778");
expression = parse("['andy']==null?1:2");
System.out.println(expression.getValue(m));
assertEquals(2, expression.getValue(m));
assertCanCompile(expression);
assertEquals(2, expression.getValue(m));
m.remove("andy");
@ -4028,7 +4101,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertEquals("C", getAst().getExitDescriptor());
// Collections
List<String> strings = new ArrayList<String>();
List<String> strings = new ArrayList<>();
strings.add("aaa");
strings.add("bbb");
strings.add("ccc");
@ -4038,7 +4111,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertEquals("bbb", expression.getValue(strings));
assertEquals("Ljava/lang/Object", getAst().getExitDescriptor());
List<Integer> ints = new ArrayList<Integer>();
List<Integer> ints = new ArrayList<>();
ints.add(123);
ints.add(456);
ints.add(789);
@ -4049,7 +4122,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertEquals("Ljava/lang/Object", getAst().getExitDescriptor());
// Maps
Map<String,Integer> map1 = new HashMap<String,Integer>();
Map<String, Integer> map1 = new HashMap<>();
map1.put("aaa", 111);
map1.put("bbb", 222);
map1.put("ccc", 333);
@ -4082,7 +4155,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
// list of arrays
List<String[]> listOfStringArrays = new ArrayList<String[]>();
List<String[]> listOfStringArrays = new ArrayList<>();
listOfStringArrays.add(new String[] {"a","b","c"});
listOfStringArrays.add(new String[] {"d","e","f"});
expression = parser.parseExpression("[1]");
@ -4097,7 +4170,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertEquals("d", stringify(expression.getValue(listOfStringArrays)));
assertEquals("Ljava/lang/String", getAst().getExitDescriptor());
List<Integer[]> listOfIntegerArrays = new ArrayList<Integer[]>();
List<Integer[]> listOfIntegerArrays = new ArrayList<>();
listOfIntegerArrays.add(new Integer[] {1,2,3});
listOfIntegerArrays.add(new Integer[] {4,5,6});
expression = parser.parseExpression("[0]");
@ -4114,11 +4187,11 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
// array of lists
List<String>[] stringArrayOfLists = new ArrayList[2];
stringArrayOfLists[0] = new ArrayList<String>();
stringArrayOfLists[0] = new ArrayList<>();
stringArrayOfLists[0].add("a");
stringArrayOfLists[0].add("b");
stringArrayOfLists[0].add("c");
stringArrayOfLists[1] = new ArrayList<String>();
stringArrayOfLists[1] = new ArrayList<>();
stringArrayOfLists[1].add("d");
stringArrayOfLists[1].add("e");
stringArrayOfLists[1].add("f");
@ -4163,13 +4236,13 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertEquals("I", getAst().getExitDescriptor());
// list of lists of reference types
List<List<String>> listOfListOfStrings = new ArrayList<List<String>>();
List<String> list = new ArrayList<String>();
List<List<String>> listOfListOfStrings = new ArrayList<>();
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
listOfListOfStrings.add(list);
list = new ArrayList<String>();
list = new ArrayList<>();
list.add("d");
list.add("e");
list.add("f");
@ -4189,8 +4262,8 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertEquals("Ljava/lang/Object", getAst().getExitDescriptor());
// Map of lists
Map<String,List<String>> mapToLists = new HashMap<String,List<String>>();
list = new ArrayList<String>();
Map<String,List<String>> mapToLists = new HashMap<>();
list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
@ -4209,7 +4282,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertEquals("Ljava/lang/Object", getAst().getExitDescriptor());
// Map to array
Map<String,int[]> mapToIntArray = new HashMap<String,int[]>();
Map<String,int[]> mapToIntArray = new HashMap<>();
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.addPropertyAccessor(new CompilableMapAccessor());
mapToIntArray.put("foo",new int[] {1,2,3});
@ -4244,7 +4317,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
// Map array
Map<String, String>[] mapArray = new Map[1];
mapArray[0] = new HashMap<String,String>();
mapArray[0] = new HashMap<>();
mapArray[0].put("key", "value1");
expression = parser.parseExpression("[0]");
assertEquals("{key=value1}", stringify(expression.getValue(mapArray)));
@ -4318,11 +4391,11 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
// v = expression.getValue(ctx,holder);
// }
// System.out.println((System.currentTimeMillis() - stime));
//
assertCanCompile(expression);
v = expression.getValue(ctx,holder);
assertEquals("abc", v);
//
// // time it compiled
// stime = System.currentTimeMillis();
// for (int i = 0; i < 100000; i++) {
@ -4334,202 +4407,202 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
public void compilerWithGenerics_12040() {
expression = parser.parseExpression("payload!=2");
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.class));
assertTrue(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(2),Boolean.class));
assertFalse(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class));
expression = parser.parseExpression("2!=payload");
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.class));
assertTrue(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(2),Boolean.class));
assertFalse(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class));
expression = parser.parseExpression("payload!=6L");
assertTrue(expression.getValue(new GenericMessageTestHelper<Long>(4L),Boolean.class));
assertTrue(expression.getValue(new GenericMessageTestHelper<>(4L), Boolean.class));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper<Long>(6L),Boolean.class));
assertFalse(expression.getValue(new GenericMessageTestHelper<>(6L), Boolean.class));
expression = parser.parseExpression("payload==2");
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.class));
assertFalse(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(2),Boolean.class));
assertTrue(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class));
expression = parser.parseExpression("2==payload");
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.class));
assertFalse(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(2),Boolean.class));
assertTrue(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class));
expression = parser.parseExpression("payload==6L");
assertFalse(expression.getValue(new GenericMessageTestHelper<Long>(4L),Boolean.class));
assertFalse(expression.getValue(new GenericMessageTestHelper<>(4L), Boolean.class));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper<Long>(6L),Boolean.class));
assertTrue(expression.getValue(new GenericMessageTestHelper<>(6L), Boolean.class));
expression = parser.parseExpression("2==payload");
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.class));
assertFalse(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(2),Boolean.class));
assertTrue(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class));
expression = parser.parseExpression("payload/2");
assertEquals(2,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertEquals(2, expression.getValue(new GenericMessageTestHelper<>(4)));
assertCanCompile(expression);
assertEquals(3,expression.getValue(new GenericMessageTestHelper<Integer>(6)));
assertEquals(3, expression.getValue(new GenericMessageTestHelper<>(6)));
expression = parser.parseExpression("100/payload");
assertEquals(25,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertEquals(25, expression.getValue(new GenericMessageTestHelper<>(4)));
assertCanCompile(expression);
assertEquals(10,expression.getValue(new GenericMessageTestHelper<Integer>(10)));
assertEquals(10, expression.getValue(new GenericMessageTestHelper<>(10)));
expression = parser.parseExpression("payload+2");
assertEquals(6,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertEquals(6, expression.getValue(new GenericMessageTestHelper<>(4)));
assertCanCompile(expression);
assertEquals(8,expression.getValue(new GenericMessageTestHelper<Integer>(6)));
assertEquals(8, expression.getValue(new GenericMessageTestHelper<>(6)));
expression = parser.parseExpression("100+payload");
assertEquals(104,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertEquals(104, expression.getValue(new GenericMessageTestHelper<>(4)));
assertCanCompile(expression);
assertEquals(110,expression.getValue(new GenericMessageTestHelper<Integer>(10)));
assertEquals(110, expression.getValue(new GenericMessageTestHelper<>(10)));
expression = parser.parseExpression("payload-2");
assertEquals(2,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertEquals(2, expression.getValue(new GenericMessageTestHelper<>(4)));
assertCanCompile(expression);
assertEquals(4,expression.getValue(new GenericMessageTestHelper<Integer>(6)));
assertEquals(4, expression.getValue(new GenericMessageTestHelper<>(6)));
expression = parser.parseExpression("100-payload");
assertEquals(96,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertEquals(96, expression.getValue(new GenericMessageTestHelper<>(4)));
assertCanCompile(expression);
assertEquals(90,expression.getValue(new GenericMessageTestHelper<Integer>(10)));
assertEquals(90, expression.getValue(new GenericMessageTestHelper<>(10)));
expression = parser.parseExpression("payload*2");
assertEquals(8,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertEquals(8, expression.getValue(new GenericMessageTestHelper<>(4)));
assertCanCompile(expression);
assertEquals(12,expression.getValue(new GenericMessageTestHelper<Integer>(6)));
assertEquals(12, expression.getValue(new GenericMessageTestHelper<>(6)));
expression = parser.parseExpression("100*payload");
assertEquals(400,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertEquals(400, expression.getValue(new GenericMessageTestHelper<>(4)));
assertCanCompile(expression);
assertEquals(1000,expression.getValue(new GenericMessageTestHelper<Integer>(10)));
assertEquals(1000, expression.getValue(new GenericMessageTestHelper<>(10)));
expression = parser.parseExpression("payload/2L");
assertEquals(2L,expression.getValue(new GenericMessageTestHelper<Long>(4L)));
assertEquals(2L, expression.getValue(new GenericMessageTestHelper<>(4L)));
assertCanCompile(expression);
assertEquals(3L,expression.getValue(new GenericMessageTestHelper<Long>(6L)));
assertEquals(3L, expression.getValue(new GenericMessageTestHelper<>(6L)));
expression = parser.parseExpression("100L/payload");
assertEquals(25L,expression.getValue(new GenericMessageTestHelper<Long>(4L)));
assertEquals(25L, expression.getValue(new GenericMessageTestHelper<>(4L)));
assertCanCompile(expression);
assertEquals(10L,expression.getValue(new GenericMessageTestHelper<Long>(10L)));
assertEquals(10L, expression.getValue(new GenericMessageTestHelper<>(10L)));
expression = parser.parseExpression("payload/2f");
assertEquals(2f,expression.getValue(new GenericMessageTestHelper<Float>(4f)));
assertEquals(2f, expression.getValue(new GenericMessageTestHelper<>(4f)));
assertCanCompile(expression);
assertEquals(3f,expression.getValue(new GenericMessageTestHelper<Float>(6f)));
assertEquals(3f, expression.getValue(new GenericMessageTestHelper<>(6f)));
expression = parser.parseExpression("100f/payload");
assertEquals(25f,expression.getValue(new GenericMessageTestHelper<Float>(4f)));
assertEquals(25f, expression.getValue(new GenericMessageTestHelper<>(4f)));
assertCanCompile(expression);
assertEquals(10f,expression.getValue(new GenericMessageTestHelper<Float>(10f)));
assertEquals(10f, expression.getValue(new GenericMessageTestHelper<>(10f)));
expression = parser.parseExpression("payload/2d");
assertEquals(2d,expression.getValue(new GenericMessageTestHelper<Double>(4d)));
assertEquals(2d, expression.getValue(new GenericMessageTestHelper<>(4d)));
assertCanCompile(expression);
assertEquals(3d,expression.getValue(new GenericMessageTestHelper<Double>(6d)));
assertEquals(3d, expression.getValue(new GenericMessageTestHelper<>(6d)));
expression = parser.parseExpression("100d/payload");
assertEquals(25d,expression.getValue(new GenericMessageTestHelper<Double>(4d)));
assertEquals(25d, expression.getValue(new GenericMessageTestHelper<>(4d)));
assertCanCompile(expression);
assertEquals(10d,expression.getValue(new GenericMessageTestHelper<Double>(10d)));
assertEquals(10d, expression.getValue(new GenericMessageTestHelper<>(10d)));
}
// The new helper class here uses an upper bound on the generic
@Test
public void compilerWithGenerics_12040_2() {
expression = parser.parseExpression("payload/2");
assertEquals(2,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertEquals(2, expression.getValue(new GenericMessageTestHelper2<>(4)));
assertCanCompile(expression);
assertEquals(3,expression.getValue(new GenericMessageTestHelper2<Integer>(6)));
assertEquals(3, expression.getValue(new GenericMessageTestHelper2<>(6)));
expression = parser.parseExpression("9/payload");
assertEquals(1,expression.getValue(new GenericMessageTestHelper2<Integer>(9)));
assertEquals(1, expression.getValue(new GenericMessageTestHelper2<>(9)));
assertCanCompile(expression);
assertEquals(3,expression.getValue(new GenericMessageTestHelper2<Integer>(3)));
assertEquals(3, expression.getValue(new GenericMessageTestHelper2<>(3)));
expression = parser.parseExpression("payload+2");
assertEquals(6,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertEquals(6, expression.getValue(new GenericMessageTestHelper2<>(4)));
assertCanCompile(expression);
assertEquals(8,expression.getValue(new GenericMessageTestHelper2<Integer>(6)));
assertEquals(8, expression.getValue(new GenericMessageTestHelper2<>(6)));
expression = parser.parseExpression("100+payload");
assertEquals(104,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertEquals(104, expression.getValue(new GenericMessageTestHelper2<>(4)));
assertCanCompile(expression);
assertEquals(110,expression.getValue(new GenericMessageTestHelper2<Integer>(10)));
assertEquals(110, expression.getValue(new GenericMessageTestHelper2<>(10)));
expression = parser.parseExpression("payload-2");
assertEquals(2,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertEquals(2, expression.getValue(new GenericMessageTestHelper2<>(4)));
assertCanCompile(expression);
assertEquals(4,expression.getValue(new GenericMessageTestHelper2<Integer>(6)));
assertEquals(4, expression.getValue(new GenericMessageTestHelper2<>(6)));
expression = parser.parseExpression("100-payload");
assertEquals(96,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertEquals(96, expression.getValue(new GenericMessageTestHelper2<>(4)));
assertCanCompile(expression);
assertEquals(90,expression.getValue(new GenericMessageTestHelper2<Integer>(10)));
assertEquals(90, expression.getValue(new GenericMessageTestHelper2<>(10)));
expression = parser.parseExpression("payload*2");
assertEquals(8,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertEquals(8, expression.getValue(new GenericMessageTestHelper2<>(4)));
assertCanCompile(expression);
assertEquals(12,expression.getValue(new GenericMessageTestHelper2<Integer>(6)));
assertEquals(12, expression.getValue(new GenericMessageTestHelper2<>(6)));
expression = parser.parseExpression("100*payload");
assertEquals(400,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertEquals(400, expression.getValue(new GenericMessageTestHelper2<>(4)));
assertCanCompile(expression);
assertEquals(1000,expression.getValue(new GenericMessageTestHelper2<Integer>(10)));
assertEquals(1000, expression.getValue(new GenericMessageTestHelper2<>(10)));
}
// The other numeric operators
@Test
public void compilerWithGenerics_12040_3() {
expression = parser.parseExpression("payload >= 2");
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(4),Boolean.TYPE));
assertTrue(expression.getValue(new GenericMessageTestHelper2<>(4), Boolean.TYPE));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper2<>(1), Boolean.TYPE));
expression = parser.parseExpression("2 >= payload");
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(5),Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper2<>(5), Boolean.TYPE));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertTrue(expression.getValue(new GenericMessageTestHelper2<>(1), Boolean.TYPE));
expression = parser.parseExpression("payload > 2");
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(4),Boolean.TYPE));
assertTrue(expression.getValue(new GenericMessageTestHelper2<>(4), Boolean.TYPE));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper2<>(1), Boolean.TYPE));
expression = parser.parseExpression("2 > payload");
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(5),Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper2<>(5), Boolean.TYPE));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertTrue(expression.getValue(new GenericMessageTestHelper2<>(1), Boolean.TYPE));
expression = parser.parseExpression("payload <=2");
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertTrue(expression.getValue(new GenericMessageTestHelper2<>(1), Boolean.TYPE));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(6),Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper2<>(6), Boolean.TYPE));
expression = parser.parseExpression("2 <= payload");
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper2<>(1), Boolean.TYPE));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(6),Boolean.TYPE));
assertTrue(expression.getValue(new GenericMessageTestHelper2<>(6), Boolean.TYPE));
expression = parser.parseExpression("payload < 2");
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertTrue(expression.getValue(new GenericMessageTestHelper2<>(1), Boolean.TYPE));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(6),Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper2<>(6), Boolean.TYPE));
expression = parser.parseExpression("2 < payload");
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertFalse(expression.getValue(new GenericMessageTestHelper2<>(1), Boolean.TYPE));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(6),Boolean.TYPE));
assertTrue(expression.getValue(new GenericMessageTestHelper2<>(6), Boolean.TYPE));
}
@Test
public void indexerMapAccessor_12045() throws Exception {
SpelParserConfiguration spc = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,this.getClass().getClassLoader());
SpelParserConfiguration spc = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,getClass().getClassLoader());
SpelExpressionParser sep = new SpelExpressionParser(spc);
expression=sep.parseExpression("headers[command]");
MyMessage root = new MyMessage();
@ -4615,8 +4688,6 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
// helper classes
public interface Message<T> {
MessageHeaders getHeaders();
@ -4627,6 +4698,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
int[] getIa();
}
public static class MyMessage implements Message<String> {
public MessageHeaders getHeaders() {
@ -4655,10 +4727,12 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
@SuppressWarnings("serial")
public static class MessageHeaders extends HashMap<String, Object> {
}
public static class GenericMessageTestHelper<T> {
private T payload;
@ -4672,6 +4746,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
// This test helper has a bound on the type variable
public static class GenericMessageTestHelper2<T extends Number> {
@ -4686,6 +4761,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
static class MyAccessor implements CompilablePropertyAccessor {
private Method method;
@ -4743,6 +4819,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
static class CompilableMapAccessor implements CompilablePropertyAccessor {
@Override
@ -4799,6 +4876,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
/**
* Exception thrown from {@code read} in order to reset a cached
* PropertyAccessor, allowing other accessors to have a try.
@ -4820,8 +4898,6 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
// test classes
public static class Greeter {
public String getWorld() {
@ -4833,16 +4909,19 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class FooObject {
public Object getObject() { return "hello"; }
}
public static class FooString {
public String getObject() { return "hello"; }
}
public static class Payload {
Two[] DR = new Two[] {new Two()};
@ -4854,6 +4933,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class Payload2 {
String var1 = "abc";
@ -4870,11 +4950,13 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class Payload2Holder {
public Payload2 payload2 = new Payload2();
}
public class Person {
private int age;
@ -4892,6 +4974,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public class Person3 {
private int age;
@ -4909,6 +4992,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class Two {
Three three = new Three();
@ -4921,6 +5005,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class Three {
double four = 0.04d;
@ -4930,6 +5015,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public class PayloadX {
public int valueI = 120;
@ -4962,12 +5048,15 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
public Short valueSB = (short)120;
public Short valueSB18 = (short)18;
public Short valueSB20 = (short)20;
public PayloadX payload;
public PayloadX() {
payload = this;
}
}
public static class TestClass1 {
public int index1 = 1;
@ -4975,6 +5064,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
public String word = "abcd";
}
public static class TestClass4 {
public boolean a,b;
@ -4984,6 +5074,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
public boolean getB() { return b; }
}
public static class TestClass10 {
public String s = null;
@ -5025,6 +5116,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class TestClass5 {
public int i = 0;
@ -5238,6 +5330,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class TestClass6 {
public String orange = "value1";
@ -5254,6 +5347,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class TestClass7 {
public static String property;
@ -5269,9 +5363,9 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
StringTokenizer st = new StringTokenizer(s);
property = st.nextToken();
}
}
public static class TestClass8 {
public int i;
@ -5287,7 +5381,6 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
public TestClass8() {
}
public TestClass8(Integer i) {
@ -5300,6 +5393,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class Obj {
private final String param1;
@ -5309,6 +5403,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class Obj2 {
public final String output;
@ -5322,6 +5417,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class Obj3 {
public final String output;
@ -5347,6 +5443,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class Obj4 {
public final String output;
@ -5360,11 +5457,14 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
@SuppressWarnings("unused")
private static class TestClass9 {
public TestClass9(int i) {}
public TestClass9(int i) {
}
}
// These test classes simulate a pattern of public/private classes seen in Spring Security
@ -5381,10 +5481,12 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
// public class SecurityContextHolderAwareRequestWrapper extends HttpServletRequestWrapper
static class SecurityContextHolderAwareRequestWrapper extends HttpServletRequestWrapper {
}
public static class HttpServletRequestWrapper {
public String getServletPath() {
@ -5392,6 +5494,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
// Here the declaring class is not public
static class SomeCompareMethod {
@ -5406,6 +5509,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class SomeCompareMethod2 {
public static int negate(int i1) {
@ -5478,6 +5582,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class DelegatingStringFormat {
public static String format(String s, Object... args) {
@ -5485,8 +5590,13 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
}
public static class StaticsHelper {
static StaticsHelper sh = new StaticsHelper();
public static StaticsHelper fielda = sh;
public static String fieldb = "fb";
public static StaticsHelper methoda() {
return sh;
}
@ -5502,12 +5612,53 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
return "pb";
}
public static StaticsHelper fielda = sh;
public static String fieldb = "fb";
public String toString() {
return "sh";
}
}
public static class Apple implements Comparable<Apple> {
public Object gotComparedTo = null;
public int i;
public Apple(int i) {
this.i = i;
}
public void setValue(int i) {
this.i = i;
}
@Override
public int compareTo(Apple that) {
this.gotComparedTo = that;
if (this.i < that.i) {
return -1;
}
else if (this.i > that.i) {
return +1;
}
else {
return 0;
}
}
}
// For opNe_SPR14863
public static class MyContext {
private final Map<String, String> data;
public MyContext(Map<String, String> data) {
this.data = data;
}
public Map<String, String> getData() {
return data;
}
}
}