Browse Source

Reduce SpEL compilation restrictions on mathematical expressions

Prior to this change the SpEL compiler would not compile mathematical
expressions where the operands were of differing types (e.g. int
and double). This required the expression writer to do the conversion
in the expression text. For example:

T(Integer).valueOf(someInt).doubleValue()/35d

With this commit the restriction is lifted and it is more like Java
so that you can simply do someInt/35d. The mathematical operators
affected are divide/plus/minus/multiply and modulus. This change
involved removing some guards so that the exitTypeDescriptor (the
trigger for whether compilation is allowed) is set more frequently
and enhancing bytecode generation to perform more sophisticated
conversion/coercion automatically.

Issue: SPR-12789
pull/753/head
Andy Clement 10 years ago
parent
commit
b7ef04767a
  1. 136
      spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java
  2. 26
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java
  3. 26
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java
  4. 26
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java
  5. 26
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java
  6. 24
      spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java
  7. 1015
      spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java
  8. 94
      spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationPerformanceTests.java

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

@ -209,6 +209,125 @@ public class CodeFlow implements Opcodes { @@ -209,6 +209,125 @@ public class CodeFlow implements Opcodes {
}
}
/**
* For numbers, use the appropriate method on the number to convert it to the primitive type requested.
* @param mv the method visitor into which instructions should be inserted
* @param targetDescriptor the primitive type desired as output
* @param stackDescriptor the descriptor of the type on top of the stack
*/
public static void insertUnboxNumberInsns(MethodVisitor mv, char targetDescriptor, String stackDescriptor) {
switch (targetDescriptor) {
case 'D':
if (stackDescriptor.equals("Ljava/lang/Object")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Number");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "doubleValue", "()D", false);
break;
case 'F':
if (stackDescriptor.equals("Ljava/lang/Object")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Number");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "floatValue", "()F", false);
break;
case 'J':
if (stackDescriptor.equals("Ljava/lang/Object")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Number");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "longValue", "()J", false);
break;
case 'I':
if (stackDescriptor.equals("Ljava/lang/Object")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Number");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "intValue", "()I", false);
break;
// does not handle Z, B, C, S
default:
throw new IllegalArgumentException("Unboxing should not be attempted for descriptor '" + targetDescriptor + "'");
}
}
/**
* Insert any necessary numeric conversion bytecodes based upon what is on the stack and the desired target type.
* @param mv the method visitor into which instructions should be placed
* @param targetDescriptor the (primitive) descriptor of the target type
* @param stackDescriptor the descriptor of the operand on top of the stack
*/
public static void insertAnyNecessaryTypeConversionBytecodes(MethodVisitor mv, char targetDescriptor, String stackDescriptor) {
if (CodeFlow.isPrimitive(stackDescriptor)) {
char stackTop = stackDescriptor.charAt(0);
if (stackTop=='I' || stackTop=='B' || stackTop=='S' || stackTop=='C') {
if (targetDescriptor=='D') {
mv.visitInsn(I2D);
}
else if (targetDescriptor=='F') {
mv.visitInsn(I2F);
}
else if (targetDescriptor=='J') {
mv.visitInsn(I2L);
}
else if (targetDescriptor=='I') {
// nop
}
else {
throw new IllegalStateException("cannot get from "+stackTop+" to "+targetDescriptor);
}
}
else if (stackTop=='J') {
if (targetDescriptor=='D') {
mv.visitInsn(L2D);
}
else if (targetDescriptor=='F') {
mv.visitInsn(L2F);
}
else if (targetDescriptor=='J') {
// nop
}
else if (targetDescriptor=='I') {
mv.visitInsn(L2I);
}
else {
throw new IllegalStateException("cannot get from "+stackTop+" to "+targetDescriptor);
}
}
else if (stackTop=='F') {
if (targetDescriptor=='D') {
mv.visitInsn(F2D);
}
else if (targetDescriptor=='F') {
// nop
}
else if (targetDescriptor=='J') {
mv.visitInsn(F2L);
}
else if (targetDescriptor=='I') {
mv.visitInsn(F2I);
}
else {
throw new IllegalStateException("cannot get from "+stackTop+" to "+targetDescriptor);
}
}
else if (stackTop=='D') {
if (targetDescriptor=='D') {
// nop
}
else if (targetDescriptor=='F') {
mv.visitInsn(D2F);
}
else if (targetDescriptor=='J') {
mv.visitInsn(D2L);
}
else if (targetDescriptor=='I') {
mv.visitInsn(D2I);
}
else {
throw new IllegalStateException("cannot get from "+stackDescriptor+" to "+targetDescriptor);
}
}
}
}
/**
* Create the JVM signature descriptor for a method. This consists of the descriptors
* for the method parameters surrounded with parentheses, followed by the
@ -836,5 +955,22 @@ public class CodeFlow implements Opcodes { @@ -836,5 +955,22 @@ public class CodeFlow implements Opcodes {
}
}
/**
* For use in mathematical operators, handles converting from a (possibly boxed) number on the stack to a primitive numeric type.
* For example, from a Integer to a double, just need to call 'Number.doubleValue()' but from an int to a double, need to use
* the bytecode 'i2d'.
* @param mv the method visitor when instructions should be appended
* @param stackDescriptor a descriptor of the operand on the stack
* @param targetDescriptor a primitive type descriptor
*/
public static void insertNumericUnboxOrPrimitiveTypeCoercion(MethodVisitor mv,
String stackDescriptor, char targetDecriptor) {
if (!CodeFlow.isPrimitive(stackDescriptor)) {
CodeFlow.insertUnboxNumberInsns(mv, targetDecriptor, stackDescriptor);
} else {
CodeFlow.insertAnyNecessaryTypeConversionBytecodes(mv, targetDecriptor, stackDescriptor);
}
}
}

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -59,15 +59,11 @@ public class OpDivide extends Operator { @@ -59,15 +59,11 @@ public class OpDivide extends Operator {
return new TypedValue(leftBigDecimal.divide(rightBigDecimal, scale, RoundingMode.HALF_EVEN));
}
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "D";
}
this.exitTypeDescriptor = "D";
return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "F";
}
this.exitTypeDescriptor = "F";
return new TypedValue(leftNumber.floatValue() / rightNumber.floatValue());
}
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
@ -76,15 +72,11 @@ public class OpDivide extends Operator { @@ -76,15 +72,11 @@ public class OpDivide extends Operator {
return new TypedValue(leftBigInteger.divide(rightBigInteger));
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "J";
}
this.exitTypeDescriptor = "J";
return new TypedValue(leftNumber.longValue() / rightNumber.longValue());
}
else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
}
this.exitTypeDescriptor = "I";
return new TypedValue(leftNumber.intValue() / rightNumber.intValue());
}
else {
@ -113,17 +105,13 @@ public class OpDivide extends Operator { @@ -113,17 +105,13 @@ public class OpDivide extends Operator {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
getLeftOperand().generateCode(mv, cf);
String leftDesc = getLeftOperand().exitTypeDescriptor;
if (!CodeFlow.isPrimitive(leftDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
}
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, leftDesc, this.exitTypeDescriptor.charAt(0));
if (this.children.length > 1) {
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
String rightDesc = getRightOperand().exitTypeDescriptor;
cf.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
}
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, rightDesc, this.exitTypeDescriptor.charAt(0));
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':
mv.visitInsn(IDIV);

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -108,15 +108,11 @@ public class OpMinus extends Operator { @@ -108,15 +108,11 @@ public class OpMinus extends Operator {
return new TypedValue(leftBigDecimal.subtract(rightBigDecimal));
}
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "D";
}
this.exitTypeDescriptor = "D";
return new TypedValue(leftNumber.doubleValue() - rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "F";
}
this.exitTypeDescriptor = "F";
return new TypedValue(leftNumber.floatValue() - rightNumber.floatValue());
}
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
@ -125,15 +121,11 @@ public class OpMinus extends Operator { @@ -125,15 +121,11 @@ public class OpMinus extends Operator {
return new TypedValue(leftBigInteger.subtract(rightBigInteger));
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "J";
}
this.exitTypeDescriptor = "J";
return new TypedValue(leftNumber.longValue() - rightNumber.longValue());
}
else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
}
this.exitTypeDescriptor = "I";
return new TypedValue(leftNumber.intValue() - rightNumber.intValue());
}
else {
@ -185,17 +177,13 @@ public class OpMinus extends Operator { @@ -185,17 +177,13 @@ public class OpMinus extends Operator {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
getLeftOperand().generateCode(mv, cf);
String leftDesc = getLeftOperand().exitTypeDescriptor;
if (!CodeFlow.isPrimitive(leftDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
}
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, leftDesc, this.exitTypeDescriptor.charAt(0));
if (this.children.length > 1) {
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
String rightDesc = getRightOperand().exitTypeDescriptor;
cf.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
}
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, rightDesc, this.exitTypeDescriptor.charAt(0));
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':
mv.visitInsn(ISUB);

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -57,15 +57,11 @@ public class OpModulus extends Operator { @@ -57,15 +57,11 @@ public class OpModulus extends Operator {
return new TypedValue(leftBigDecimal.remainder(rightBigDecimal));
}
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "D";
}
this.exitTypeDescriptor = "D";
return new TypedValue(leftNumber.doubleValue() % rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "F";
}
this.exitTypeDescriptor = "F";
return new TypedValue(leftNumber.floatValue() % rightNumber.floatValue());
}
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
@ -74,15 +70,11 @@ public class OpModulus extends Operator { @@ -74,15 +70,11 @@ public class OpModulus extends Operator {
return new TypedValue(leftBigInteger.remainder(rightBigInteger));
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "J";
}
this.exitTypeDescriptor = "J";
return new TypedValue(leftNumber.longValue() % rightNumber.longValue());
}
else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
}
this.exitTypeDescriptor = "I";
return new TypedValue(leftNumber.intValue() % rightNumber.intValue());
}
else {
@ -111,17 +103,13 @@ public class OpModulus extends Operator { @@ -111,17 +103,13 @@ public class OpModulus extends Operator {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
getLeftOperand().generateCode(mv, cf);
String leftDesc = getLeftOperand().exitTypeDescriptor;
if (!CodeFlow.isPrimitive(leftDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
}
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, leftDesc, this.exitTypeDescriptor.charAt(0));
if (this.children.length > 1) {
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
String rightDesc = getRightOperand().exitTypeDescriptor;
cf.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
}
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, rightDesc, this.exitTypeDescriptor.charAt(0));
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':
mv.visitInsn(IREM);

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -81,15 +81,11 @@ public class OpMultiply extends Operator { @@ -81,15 +81,11 @@ public class OpMultiply extends Operator {
return new TypedValue(leftBigDecimal.multiply(rightBigDecimal));
}
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "D";
}
this.exitTypeDescriptor = "D";
return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "F";
}
this.exitTypeDescriptor = "F";
return new TypedValue(leftNumber.floatValue() * rightNumber.floatValue());
}
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
@ -98,15 +94,11 @@ public class OpMultiply extends Operator { @@ -98,15 +94,11 @@ public class OpMultiply extends Operator {
return new TypedValue(leftBigInteger.multiply(rightBigInteger));
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "J";
}
this.exitTypeDescriptor = "J";
return new TypedValue(leftNumber.longValue() * rightNumber.longValue());
}
else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
}
this.exitTypeDescriptor = "I";
return new TypedValue(leftNumber.intValue() * rightNumber.intValue());
}
else {
@ -144,17 +136,13 @@ public class OpMultiply extends Operator { @@ -144,17 +136,13 @@ public class OpMultiply extends Operator {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
getLeftOperand().generateCode(mv, cf);
String leftDesc = getLeftOperand().exitTypeDescriptor;
if (!CodeFlow.isPrimitive(leftDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
}
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, leftDesc, this.exitTypeDescriptor.charAt(0));
if (this.children.length > 1) {
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
String rightDesc = getRightOperand().exitTypeDescriptor;
cf.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
}
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, rightDesc, this.exitTypeDescriptor.charAt(0));
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':
mv.visitInsn(IMUL);

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

@ -95,15 +95,11 @@ public class OpPlus extends Operator { @@ -95,15 +95,11 @@ public class OpPlus extends Operator {
return new TypedValue(leftBigDecimal.add(rightBigDecimal));
}
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "D";
}
this.exitTypeDescriptor = "D";
return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "F";
}
this.exitTypeDescriptor = "F";
return new TypedValue(leftNumber.floatValue() + rightNumber.floatValue());
}
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
@ -112,15 +108,11 @@ public class OpPlus extends Operator { @@ -112,15 +108,11 @@ public class OpPlus extends Operator {
return new TypedValue(leftBigInteger.add(rightBigInteger));
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "J";
}
this.exitTypeDescriptor = "J";
return new TypedValue(leftNumber.longValue() + rightNumber.longValue());
}
else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
}
this.exitTypeDescriptor = "I";
return new TypedValue(leftNumber.intValue() + rightNumber.intValue());
}
else {
@ -227,17 +219,13 @@ public class OpPlus extends Operator { @@ -227,17 +219,13 @@ public class OpPlus extends Operator {
else {
getLeftOperand().generateCode(mv, cf);
String leftDesc = getLeftOperand().exitTypeDescriptor;
if (!CodeFlow.isPrimitive(leftDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
}
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, leftDesc, this.exitTypeDescriptor.charAt(0));
if (this.children.length > 1) {
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
String rightDesc = getRightOperand().exitTypeDescriptor;
cf.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
}
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, rightDesc, this.exitTypeDescriptor.charAt(0));
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':
mv.visitInsn(IADD);

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

File diff suppressed because it is too large Load Diff

94
spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationPerformanceTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2015 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.
@ -63,6 +63,98 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests { @@ -63,6 +63,98 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
}
}
public static class NumberHolder {
public int payload = 36;
}
/**
* This test verifies the new support for compiling mathematical expressions with
* different operand types.
*/
@Test
public void compilingMathematicalExpressionsWithDifferentOperandTypes() throws Exception {
NumberHolder nh = new NumberHolder();
expression = parser.parseExpression("(T(Integer).valueOf(payload).doubleValue())/18D");
Object o = expression.getValue(nh);
assertEquals(2d,o);
System.out.println("Performance check for SpEL expression: '(T(Integer).valueOf(payload).doubleValue())/18D'");
long stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
compile(expression);
System.out.println("Now compiled:");
o = expression.getValue(nh);
assertEquals(2d, o);
stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
expression = parser.parseExpression("payload/18D");
o = expression.getValue(nh);
assertEquals(2d,o);
System.out.println("Performance check for SpEL expression: 'payload/18D'");
stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
compile(expression);
System.out.println("Now compiled:");
o = expression.getValue(nh);
assertEquals(2d, o);
stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
stime = System.currentTimeMillis();
for (int i=0;i<1000000;i++) {
o = expression.getValue(nh);
}
System.out.println("One million iterations: "+(System.currentTimeMillis()-stime)+"ms");
}
@Test
public void inlineLists() throws Exception {
expression = parser.parseExpression("{'abcde','ijklm'}[0].substring({1,3,4}[0],{1,3,4}[1])");

Loading…
Cancel
Save