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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 java.math.BigDecimal;
|
||||||
|
|
||||||
|
import org.springframework.asm.MethodVisitor;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
import org.springframework.expression.Operation;
|
import org.springframework.expression.Operation;
|
||||||
import org.springframework.expression.TypedValue;
|
import org.springframework.expression.TypedValue;
|
||||||
|
import org.springframework.expression.spel.CodeFlow;
|
||||||
import org.springframework.expression.spel.ExpressionState;
|
import org.springframework.expression.spel.ExpressionState;
|
||||||
import org.springframework.util.NumberUtils;
|
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) {
|
||||||
|
if (leftNumber instanceof Double && rightNumber instanceof Double) {
|
||||||
|
this.exitTypeDescriptor = "D";
|
||||||
|
}
|
||||||
return new TypedValue(leftNumber.doubleValue() % rightNumber.doubleValue());
|
return new TypedValue(leftNumber.doubleValue() % rightNumber.doubleValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leftNumber instanceof Float || rightNumber instanceof Float) {
|
if (leftNumber instanceof Float || rightNumber instanceof Float) {
|
||||||
|
if (leftNumber instanceof Float && rightNumber instanceof Float) {
|
||||||
|
this.exitTypeDescriptor = "F";
|
||||||
|
}
|
||||||
return new TypedValue(leftNumber.floatValue() % rightNumber.floatValue());
|
return new TypedValue(leftNumber.floatValue() % rightNumber.floatValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leftNumber instanceof Long || rightNumber instanceof Long) {
|
if (leftNumber instanceof Long || rightNumber instanceof Long) {
|
||||||
|
if (leftNumber instanceof Long && rightNumber instanceof Long) {
|
||||||
|
this.exitTypeDescriptor = "J";
|
||||||
|
}
|
||||||
return new TypedValue(leftNumber.longValue() % rightNumber.longValue());
|
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 new TypedValue(leftNumber.intValue() % rightNumber.intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.operate(Operation.MODULUS, leftOperand, rightOperand);
|
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());
|
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
|
@Test
|
||||||
public void constructorReference() throws Exception {
|
public void constructorReference() throws Exception {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue