Browse Source

Cope with generic methods during SpEL compilation

This commit allows the SpEL compiler to cope with generic methods
being used in expressions involving numeric operands. Due to the
use of unbound type variables the methods may look like they
return Object but in fact they are returning objects of a numeric
type that are suitable for compilation. The changes here ensure
the runtime types are looked at if the discovered declared types
are not providing enough information. This impacts all the
operands involving numerics (mathematical and relational).

Issue: SPR-12040
pull/602/merge
Andy Clement 10 years ago
parent
commit
59080ff2b2
  1. 22
      spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java
  2. 6
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java
  3. 25
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java
  4. 4
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java
  5. 3
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java
  6. 4
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java
  7. 3
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java
  8. 6
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java
  9. 6
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java
  10. 24
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java
  11. 6
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java
  12. 87
      spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java
  13. 222
      spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java

22
spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java

@ -106,61 +106,59 @@ public class CodeFlow implements Opcodes { @@ -106,61 +106,59 @@ public class CodeFlow implements Opcodes {
}
}
/**
* Insert any necessary cast and value call to convert from a boxed type to a
* primitive value
* @param mv the method visitor into which instructions should be inserted
* @param ch the primitive type desired as output
* @param isObject indicates whether the type on the stack is being thought of
* as Object (and so requires a cast)
* @param stackDescriptor the descriptor of the type on top of the stack
*/
public static void insertUnboxInsns(MethodVisitor mv, char ch, boolean isObject) {
public static void insertUnboxInsns(MethodVisitor mv, char ch, String stackDescriptor) {
switch (ch) {
case 'I':
if (isObject) {
if (!stackDescriptor.equals("Ljava/lang/Integer")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
break;
case 'Z':
if (isObject) {
if (!stackDescriptor.equals("Ljava/lang/Boolean")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
break;
case 'B':
if (isObject) {
if (!stackDescriptor.equals("Ljava/lang/Byte")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false);
break;
case 'C':
if (isObject) {
if (!stackDescriptor.equals("Ljava/lang/Character")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
break;
case 'D':
if (isObject) {
if (!stackDescriptor.equals("Ljava/lang/Double")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
break;
case 'S':
if (isObject) {
if (!stackDescriptor.equals("Ljava/lang/Short")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false);
break;
case 'F':
if (isObject) {
if (!stackDescriptor.equals("Ljava/lang/Float")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false);
break;
case 'J':
if (isObject) {
if (!stackDescriptor.equals("Ljava/lang/Long")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);

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

@ -107,13 +107,15 @@ public class OpDivide extends Operator { @@ -107,13 +107,15 @@ public class OpDivide extends Operator {
getLeftOperand().generateCode(mv, codeflow);
String leftdesc = getLeftOperand().getExitDescriptor();
if (!CodeFlow.isPrimitive(leftdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), false);
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc);
}
if (this.children.length > 1) {
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
String rightdesc = getRightOperand().getExitDescriptor();
codeflow.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), false);
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc);
}
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':

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

@ -40,6 +40,8 @@ public class OpEQ extends Operator { @@ -40,6 +40,8 @@ public class OpEQ extends Operator {
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
return BooleanTypedValue.forValue(equalityCheck(state, left, right));
}
@ -54,15 +56,14 @@ public class OpEQ extends Operator { @@ -54,15 +56,14 @@ public class OpEQ extends Operator {
}
String leftdesc = left.getExitDescriptor();
String rightdesc = right.getExitDescriptor();
if ((CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(leftdesc) ||
CodeFlow.isPrimitiveOrUnboxableSupportedNumber(rightdesc))) {
if (!CodeFlow.areBoxingCompatible(leftdesc, rightdesc)) {
return false;
}
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftdesc, rightdesc, leftActualDescriptor, rightActualDescriptor);
if (dc.areNumbers) {
return dc.areCompatible;
}
return true;
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
String leftDesc = getLeftOperand().getExitDescriptor();
@ -72,19 +73,21 @@ public class OpEQ extends Operator { @@ -72,19 +73,21 @@ public class OpEQ extends Operator {
boolean leftPrim = CodeFlow.isPrimitive(leftDesc);
boolean rightPrim = CodeFlow.isPrimitive(rightDesc);
if ((CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(leftDesc) ||
CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rightDesc)) &&
CodeFlow.areBoxingCompatible(leftDesc,rightDesc)) {
char targetType = CodeFlow.toPrimitiveTargetDesc(leftDesc);
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor);
if (dc.areNumbers && dc.areCompatible) {
char targetType = dc.compatibleType;
getLeftOperand().generateCode(mv, codeflow);
if (!leftPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, false);
CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
}
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
codeflow.exitCompilationScope();
if (!rightPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, false);
CodeFlow.insertUnboxInsns(mv, targetType, rightDesc);
}
// assert: SpelCompiler.boxingCompatible(leftDesc, rightDesc)
if (targetType=='D') {

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

@ -44,6 +44,10 @@ public class OpGE extends Operator { @@ -44,6 +44,10 @@ public class OpGE extends Operator {
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;

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

@ -45,6 +45,9 @@ public class OpGT extends Operator { @@ -45,6 +45,9 @@ public class OpGT extends Operator {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;

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

@ -46,6 +46,10 @@ public class OpLE extends Operator { @@ -46,6 +46,10 @@ public class OpLE extends Operator {
throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;

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

@ -45,6 +45,9 @@ public class OpLT extends Operator { @@ -45,6 +45,9 @@ public class OpLT extends Operator {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;

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

@ -169,13 +169,15 @@ public class OpMinus extends Operator { @@ -169,13 +169,15 @@ public class OpMinus extends Operator {
getLeftOperand().generateCode(mv, codeflow);
String leftdesc = getLeftOperand().getExitDescriptor();
if (!CodeFlow.isPrimitive(leftdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), false);
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc);
}
if (this.children.length>1) {
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
String rightdesc = getRightOperand().getExitDescriptor();
codeflow.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), false);
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc);
}
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':

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

@ -138,13 +138,15 @@ public class OpMultiply extends Operator { @@ -138,13 +138,15 @@ public class OpMultiply extends Operator {
getLeftOperand().generateCode(mv, codeflow);
String leftdesc = getLeftOperand().getExitDescriptor();
if (!CodeFlow.isPrimitive(leftdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), false);
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc);
}
if (this.children.length>1) {
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
String rightdesc = getRightOperand().getExitDescriptor();
codeflow.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), false);
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc);
}
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':

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

@ -40,6 +40,8 @@ public class OpNE extends Operator { @@ -40,6 +40,8 @@ public class OpNE extends Operator {
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
return BooleanTypedValue.forValue(!equalityCheck(state, left, right));
}
@ -54,11 +56,9 @@ public class OpNE extends Operator { @@ -54,11 +56,9 @@ public class OpNE extends Operator {
}
String leftdesc = left.getExitDescriptor();
String rightdesc = right.getExitDescriptor();
if ((CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(leftdesc) ||
CodeFlow.isPrimitiveOrUnboxableSupportedNumber(rightdesc))) {
if (!CodeFlow.areBoxingCompatible(leftdesc, rightdesc)) {
return false;
}
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftdesc, rightdesc, leftActualDescriptor, rightActualDescriptor);
if (dc.areNumbers) {
return dc.areCompatible;
}
return true;
}
@ -72,19 +72,21 @@ public class OpNE extends Operator { @@ -72,19 +72,21 @@ public class OpNE extends Operator {
boolean leftPrim = CodeFlow.isPrimitive(leftDesc);
boolean rightPrim = CodeFlow.isPrimitive(rightDesc);
if ((CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(leftDesc) ||
CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rightDesc)) &&
CodeFlow.areBoxingCompatible(leftDesc,rightDesc)) {
char targetType = CodeFlow.toPrimitiveTargetDesc(leftDesc);
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor);
if (dc.areNumbers && dc.areCompatible) {
char targetType = dc.compatibleType;
getLeftOperand().generateCode(mv, codeflow);
if (!leftPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, false);
CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
}
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
codeflow.exitCompilationScope();
if (!rightPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, false);
CodeFlow.insertUnboxInsns(mv, targetType, rightDesc);
}
// assert: SpelCompiler.boxingCompatible(leftDesc, rightDesc)
if (targetType == 'D') {

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

@ -196,13 +196,15 @@ public class OpPlus extends Operator { @@ -196,13 +196,15 @@ public class OpPlus extends Operator {
getLeftOperand().generateCode(mv, codeflow);
String leftdesc = getLeftOperand().getExitDescriptor();
if (!CodeFlow.isPrimitive(leftdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), false);
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc);
}
if (this.children.length>1) {
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
String rightdesc = getRightOperand().getExitDescriptor();
codeflow.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), false);
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc);
}
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':

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

@ -39,7 +39,12 @@ import org.springframework.util.ObjectUtils; @@ -39,7 +39,12 @@ import org.springframework.util.ObjectUtils;
public abstract class Operator extends SpelNodeImpl {
private final String operatorName;
// The descriptors of the runtime operand values are used if the discovered declared
// descriptors are not providing enough information (for example a generic type
// whose accessors seem to only be returning 'Object' - the actual descriptors may
// indicate 'int')
protected String leftActualDescriptor, rightActualDescriptor;
public Operator(String payload,int pos,SpelNodeImpl... operands) {
super(pos, operands);
@ -84,10 +89,9 @@ public abstract class Operator extends SpelNodeImpl { @@ -84,10 +89,9 @@ public abstract class Operator extends SpelNodeImpl {
// Supported operand types for equals (at the moment)
String leftDesc = left.getExitDescriptor();
String rightDesc= right.getExitDescriptor();
if (CodeFlow.isPrimitiveOrUnboxableSupportedNumber(leftDesc) && CodeFlow.isPrimitiveOrUnboxableSupportedNumber(rightDesc)) {
if (CodeFlow.areBoxingCompatible(leftDesc, rightDesc)) {
return true;
}
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor);
if (dc.areNumbers) {
return dc.areCompatible;
}
return false;
}
@ -103,16 +107,19 @@ public abstract class Operator extends SpelNodeImpl { @@ -103,16 +107,19 @@ public abstract class Operator extends SpelNodeImpl {
boolean unboxLeft = !CodeFlow.isPrimitive(leftDesc);
boolean unboxRight = !CodeFlow.isPrimitive(rightDesc);
char targetType = CodeFlow.toPrimitiveTargetDesc(leftDesc);
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor);
char targetType = dc.compatibleType;//CodeFlow.toPrimitiveTargetDesc(leftDesc);
getLeftOperand().generateCode(mv, codeflow);
if (unboxLeft) {
CodeFlow.insertUnboxInsns(mv, targetType, false);
CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
}
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
codeflow.exitCompilationScope();
if (unboxRight) {
CodeFlow.insertUnboxInsns(mv, targetType, false);
CodeFlow.insertUnboxInsns(mv, targetType, rightDesc);
}
// assert: SpelCompiler.boxingCompatible(leftDesc, rightDesc)
Label elseTarget = new Label();
@ -187,5 +194,69 @@ public abstract class Operator extends SpelNodeImpl { @@ -187,5 +194,69 @@ public abstract class Operator extends SpelNodeImpl {
return false;
}
/**
* A descriptor comparison encapsulates the result of comparing descriptor for two operands and
* describes at what level they are compatible.
*/
protected static class DescriptorComparison {
static DescriptorComparison NOT_NUMBERS = new DescriptorComparison(false,false,' ');
static DescriptorComparison INCOMPATIBLE_NUMBERS = new DescriptorComparison(true,false,' ');
final boolean areNumbers; // Were the two compared descriptor both for numbers?
final boolean areCompatible; // If they were numbers, were they compatible?
final char compatibleType; // When compatible, what is the descriptor of the common type
private DescriptorComparison(boolean areNumbers, boolean areCompatible, char compatibleType) {
this.areNumbers = areNumbers;
this.areCompatible = areCompatible;
this.compatibleType = compatibleType;
}
/**
* Returns an object that indicates whether the input descriptors are compatible. A declared descriptor
* is what could statically be determined (e.g. from looking at the return value of a property accessor
* method) whilst an actual descriptor is the type of an actual object that was returned, which may differ.
* For generic types with unbound type variables the declared descriptor discovered may be 'Object' but
* from the actual descriptor it is possible to observe that the objects are really numeric values (e.g.
* ints).
*
* @param leftDeclaredDescriptor the statically determinable left descriptor
* @param rightDeclaredDescriptor the statically determinable right descriptor
* @param leftActualDescriptor the dynamic/runtime left object descriptor
* @param rightActualDescriptor the dynamic/runtime right object descriptor
* @return a DescriptorComparison object indicating the type of compatibility, if any
*/
public static DescriptorComparison checkNumericCompatibility(String leftDeclaredDescriptor, String rightDeclaredDescriptor, String leftActualDescriptor, String rightActualDescriptor) {
String ld = leftDeclaredDescriptor;
String rd = rightDeclaredDescriptor;
boolean leftNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(ld) ;
boolean rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd);
// If the declared descriptors aren't providing the information, try the actual descriptors
if (!leftNumeric && !ld.equals(leftActualDescriptor)) {
ld = leftActualDescriptor;
leftNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(ld);
}
if (!rightNumeric && !rd.equals(rightActualDescriptor)) {
rd = rightActualDescriptor;
rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd);
}
if (leftNumeric && rightNumeric) {
if (CodeFlow.areBoxingCompatible(ld, rd)) {
return new DescriptorComparison(true, true, CodeFlow.toPrimitiveTargetDesc(ld));
}
else {
return DescriptorComparison.INCOMPATIBLE_NUMBERS;
}
}
else {
return DescriptorComparison.NOT_NUMBERS;
}
}
}
}

222
spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java

@ -2200,8 +2200,230 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { @@ -2200,8 +2200,230 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
// }
// System.out.println((System.currentTimeMillis()-stime));
}
@Test
public void compilerWithGenerics_12040() {
expression = parser.parseExpression("payload!=2");
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.class));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(2),Boolean.class));
expression = parser.parseExpression("2!=payload");
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.class));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(2),Boolean.class));
expression = parser.parseExpression("payload!=6L");
assertTrue(expression.getValue(new GenericMessageTestHelper<Long>(4L),Boolean.class));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper<Long>(6L),Boolean.class));
expression = parser.parseExpression("payload==2");
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.class));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(2),Boolean.class));
expression = parser.parseExpression("2==payload");
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.class));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(2),Boolean.class));
expression = parser.parseExpression("payload==6L");
assertFalse(expression.getValue(new GenericMessageTestHelper<Long>(4L),Boolean.class));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper<Long>(6L),Boolean.class));
expression = parser.parseExpression("2==payload");
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.class));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(2),Boolean.class));
expression = parser.parseExpression("payload/2");
assertEquals(2,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertCanCompile(expression);
assertEquals(3,expression.getValue(new GenericMessageTestHelper<Integer>(6)));
expression = parser.parseExpression("100/payload");
assertEquals(25,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertCanCompile(expression);
assertEquals(10,expression.getValue(new GenericMessageTestHelper<Integer>(10)));
expression = parser.parseExpression("payload+2");
assertEquals(6,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertCanCompile(expression);
assertEquals(8,expression.getValue(new GenericMessageTestHelper<Integer>(6)));
expression = parser.parseExpression("100+payload");
assertEquals(104,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertCanCompile(expression);
assertEquals(110,expression.getValue(new GenericMessageTestHelper<Integer>(10)));
expression = parser.parseExpression("payload-2");
assertEquals(2,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertCanCompile(expression);
assertEquals(4,expression.getValue(new GenericMessageTestHelper<Integer>(6)));
expression = parser.parseExpression("100-payload");
assertEquals(96,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertCanCompile(expression);
assertEquals(90,expression.getValue(new GenericMessageTestHelper<Integer>(10)));
expression = parser.parseExpression("payload*2");
assertEquals(8,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertCanCompile(expression);
assertEquals(12,expression.getValue(new GenericMessageTestHelper<Integer>(6)));
expression = parser.parseExpression("100*payload");
assertEquals(400,expression.getValue(new GenericMessageTestHelper<Integer>(4)));
assertCanCompile(expression);
assertEquals(1000,expression.getValue(new GenericMessageTestHelper<Integer>(10)));
expression = parser.parseExpression("payload/2L");
assertEquals(2L,expression.getValue(new GenericMessageTestHelper<Long>(4L)));
assertCanCompile(expression);
assertEquals(3L,expression.getValue(new GenericMessageTestHelper<Long>(6L)));
expression = parser.parseExpression("100L/payload");
assertEquals(25L,expression.getValue(new GenericMessageTestHelper<Long>(4L)));
assertCanCompile(expression);
assertEquals(10L,expression.getValue(new GenericMessageTestHelper<Long>(10L)));
expression = parser.parseExpression("payload/2f");
assertEquals(2f,expression.getValue(new GenericMessageTestHelper<Float>(4f)));
assertCanCompile(expression);
assertEquals(3f,expression.getValue(new GenericMessageTestHelper<Float>(6f)));
expression = parser.parseExpression("100f/payload");
assertEquals(25f,expression.getValue(new GenericMessageTestHelper<Float>(4f)));
assertCanCompile(expression);
assertEquals(10f,expression.getValue(new GenericMessageTestHelper<Float>(10f)));
expression = parser.parseExpression("payload/2d");
assertEquals(2d,expression.getValue(new GenericMessageTestHelper<Double>(4d)));
assertCanCompile(expression);
assertEquals(3d,expression.getValue(new GenericMessageTestHelper<Double>(6d)));
expression = parser.parseExpression("100d/payload");
assertEquals(25d,expression.getValue(new GenericMessageTestHelper<Double>(4d)));
assertCanCompile(expression);
assertEquals(10d,expression.getValue(new GenericMessageTestHelper<Double>(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)));
assertCanCompile(expression);
assertEquals(3,expression.getValue(new GenericMessageTestHelper2<Integer>(6)));
expression = parser.parseExpression("9/payload");
assertEquals(1,expression.getValue(new GenericMessageTestHelper2<Integer>(9)));
assertCanCompile(expression);
assertEquals(3,expression.getValue(new GenericMessageTestHelper2<Integer>(3)));
expression = parser.parseExpression("payload+2");
assertEquals(6,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertCanCompile(expression);
assertEquals(8,expression.getValue(new GenericMessageTestHelper2<Integer>(6)));
expression = parser.parseExpression("100+payload");
assertEquals(104,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertCanCompile(expression);
assertEquals(110,expression.getValue(new GenericMessageTestHelper2<Integer>(10)));
expression = parser.parseExpression("payload-2");
assertEquals(2,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertCanCompile(expression);
assertEquals(4,expression.getValue(new GenericMessageTestHelper2<Integer>(6)));
expression = parser.parseExpression("100-payload");
assertEquals(96,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertCanCompile(expression);
assertEquals(90,expression.getValue(new GenericMessageTestHelper2<Integer>(10)));
expression = parser.parseExpression("payload*2");
assertEquals(8,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertCanCompile(expression);
assertEquals(12,expression.getValue(new GenericMessageTestHelper2<Integer>(6)));
expression = parser.parseExpression("100*payload");
assertEquals(400,expression.getValue(new GenericMessageTestHelper2<Integer>(4)));
assertCanCompile(expression);
assertEquals(1000,expression.getValue(new GenericMessageTestHelper2<Integer>(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));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
expression = parser.parseExpression("2 >= payload");
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(5),Boolean.TYPE));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
expression = parser.parseExpression("payload > 2");
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(4),Boolean.TYPE));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
expression = parser.parseExpression("2 > payload");
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(5),Boolean.TYPE));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
expression = parser.parseExpression("payload <=2");
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(6),Boolean.TYPE));
expression = parser.parseExpression("2 <= payload");
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(6),Boolean.TYPE));
expression = parser.parseExpression("payload < 2");
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertCanCompile(expression);
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(6),Boolean.TYPE));
expression = parser.parseExpression("2 < payload");
assertFalse(expression.getValue(new GenericMessageTestHelper2<Integer>(1),Boolean.TYPE));
assertCanCompile(expression);
assertTrue(expression.getValue(new GenericMessageTestHelper2<Integer>(6),Boolean.TYPE));
}
// ---
public static class GenericMessageTestHelper<T> {
private T payload;
GenericMessageTestHelper(T value) {
this.payload = value;
}
public T getPayload() {
return payload;
}
}
// This test helper has a bound on the type variable
public static class GenericMessageTestHelper2<T extends Number> {
private T payload;
GenericMessageTestHelper2(T value) {
this.payload = value;
}
public T getPayload() {
return payload;
}
}
static class MyAccessor implements CompilablePropertyAccessor {
private Method method;

Loading…
Cancel
Save