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
This commit is contained in:
parent
7fbc951691
commit
b7ef04767a
|
@ -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 {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
|||
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 {
|
|||
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 {
|
|||
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);
|
||||
|
|
|
@ -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 {
|
|||
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 {
|
|||
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 {
|
|||
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);
|
||||
|
|
|
@ -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 {
|
|||
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 {
|
|||
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 {
|
|||
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);
|
||||
|
|
|
@ -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 {
|
|||
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 {
|
|||
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 {
|
|||
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);
|
||||
|
|
|
@ -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 {
|
|||
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 {
|
|||
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);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 {
|
|||
}
|
||||
}
|
||||
|
||||
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…
Reference in New Issue