diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java index c1c71326c4d..2cc81ced75b 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java @@ -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); + } } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java index 22489f29d74..28b348e770b 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java @@ -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(4),Boolean.TYPE)); + assertFalse(expression.getValue(new GenericMessageTestHelper(5),Boolean.TYPE)); + assertCanCompile(expression); + assertTrue(expression.getValue(new GenericMessageTestHelper(4),Boolean.TYPE)); + assertFalse(expression.getValue(new GenericMessageTestHelper(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 {