Support compilation of the SpEL operator OpModulus
This commit enables the modulus operator to be compiled when it is used as part of a SpEL expression. Issue: SPR-12041
This commit is contained in:
parent
59080ff2b2
commit
d30174897d
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
|
|
@ -18,9 +18,11 @@ package org.springframework.expression.spel.ast;
|
|||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
|
|
@ -54,21 +56,81 @@ public class OpModulus extends Operator {
|
|||
}
|
||||
|
||||
if (leftNumber instanceof Double || rightNumber instanceof Double) {
|
||||
if (leftNumber instanceof Double && rightNumber instanceof Double) {
|
||||
this.exitTypeDescriptor = "D";
|
||||
}
|
||||
return new TypedValue(leftNumber.doubleValue() % rightNumber.doubleValue());
|
||||
}
|
||||
|
||||
if (leftNumber instanceof Float || rightNumber instanceof Float) {
|
||||
if (leftNumber instanceof Float && rightNumber instanceof Float) {
|
||||
this.exitTypeDescriptor = "F";
|
||||
}
|
||||
return new TypedValue(leftNumber.floatValue() % rightNumber.floatValue());
|
||||
}
|
||||
|
||||
if (leftNumber instanceof Long || rightNumber instanceof Long) {
|
||||
if (leftNumber instanceof Long && rightNumber instanceof Long) {
|
||||
this.exitTypeDescriptor = "J";
|
||||
}
|
||||
return new TypedValue(leftNumber.longValue() % rightNumber.longValue());
|
||||
}
|
||||
|
||||
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
|
||||
this.exitTypeDescriptor = "I";
|
||||
}
|
||||
return new TypedValue(leftNumber.intValue() % rightNumber.intValue());
|
||||
}
|
||||
|
||||
return state.operate(Operation.MODULUS, leftOperand, rightOperand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompilable() {
|
||||
if (!getLeftOperand().isCompilable()) {
|
||||
return false;
|
||||
}
|
||||
if (this.children.length>1) {
|
||||
if (!getRightOperand().isCompilable()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return this.exitTypeDescriptor!=null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
|
||||
getLeftOperand().generateCode(mv, codeflow);
|
||||
String leftdesc = getLeftOperand().getExitDescriptor();
|
||||
if (!CodeFlow.isPrimitive(leftdesc)) {
|
||||
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), rightdesc);
|
||||
}
|
||||
switch (this.exitTypeDescriptor.charAt(0)) {
|
||||
case 'I':
|
||||
mv.visitInsn(IREM);
|
||||
break;
|
||||
case 'J':
|
||||
mv.visitInsn(LREM);
|
||||
break;
|
||||
case 'F':
|
||||
mv.visitInsn(FREM);
|
||||
break;
|
||||
case 'D':
|
||||
mv.visitInsn(DREM);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'");
|
||||
}
|
||||
}
|
||||
codeflow.pushDescriptor(this.exitTypeDescriptor);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1419,6 +1419,77 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
|||
assertEquals(-2.0f,expression.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void opModulus_12041() throws Exception {
|
||||
expression = parse("2%2");
|
||||
assertEquals(0,expression.getValue());
|
||||
assertCanCompile(expression);
|
||||
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));
|
||||
assertCanCompile(expression);
|
||||
assertTrue(expression.getValue(new GenericMessageTestHelper<Integer>(4),Boolean.TYPE));
|
||||
assertFalse(expression.getValue(new GenericMessageTestHelper<Integer>(5),Boolean.TYPE));
|
||||
|
||||
expression = parse("8%3");
|
||||
assertEquals(2,expression.getValue());
|
||||
assertCanCompile(expression);
|
||||
assertEquals(2,expression.getValue());
|
||||
|
||||
expression = parse("17L%5L");
|
||||
assertEquals(2L,expression.getValue());
|
||||
assertCanCompile(expression);
|
||||
assertEquals(2L,expression.getValue());
|
||||
|
||||
expression = parse("3.0f%2.0f");
|
||||
assertEquals(1.0f,expression.getValue());
|
||||
assertCanCompile(expression);
|
||||
assertEquals(1.0f,expression.getValue());
|
||||
|
||||
expression = parse("3.0d%4.0d");
|
||||
assertEquals(3.0d,expression.getValue());
|
||||
assertCanCompile(expression);
|
||||
assertEquals(3.0d,expression.getValue());
|
||||
|
||||
expression = parse("T(Float).valueOf(6.0f)%2");
|
||||
assertEquals(0.0f,expression.getValue());
|
||||
assertCantCompile(expression);
|
||||
|
||||
expression = parse("T(Float).valueOf(6.0f)%4");
|
||||
assertEquals(2.0f,expression.getValue());
|
||||
assertCantCompile(expression);
|
||||
|
||||
expression = parse("T(Float).valueOf(8.0f)%T(Float).valueOf(3.0f)");
|
||||
assertEquals(2.0f,expression.getValue());
|
||||
assertCanCompile(expression);
|
||||
assertEquals(2.0f,expression.getValue());
|
||||
|
||||
expression = parse("13L%T(Long).valueOf(4L)");
|
||||
assertEquals(1L,expression.getValue());
|
||||
assertCanCompile(expression);
|
||||
assertEquals(1L,expression.getValue());
|
||||
|
||||
expression = parse("T(Long).valueOf(44L)%12");
|
||||
assertEquals(8L,expression.getValue());
|
||||
assertCantCompile(expression);
|
||||
|
||||
expression = parse("T(Long).valueOf(9L)%T(Long).valueOf(2L)");
|
||||
assertEquals(1L,expression.getValue());
|
||||
assertCanCompile(expression);
|
||||
assertEquals(1L,expression.getValue());
|
||||
|
||||
expression = parse("7L%T(Long).valueOf(2L)");
|
||||
assertEquals(1L,expression.getValue());
|
||||
assertCanCompile(expression);
|
||||
assertEquals(1L,expression.getValue());
|
||||
|
||||
expression = parse("T(Float).valueOf(9.0f)%-T(Float).valueOf(4.0f)");
|
||||
assertEquals(1.0f,expression.getValue());
|
||||
assertCanCompile(expression);
|
||||
assertEquals(1.0f,expression.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorReference() throws Exception {
|
||||
|
|
|
|||
Loading…
Reference in New Issue