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 e690c46d188..0fbd98ab010 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 @@ -160,7 +160,3346 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { @Nested - class VariableReferenceTests { + class MiscellaneousTests { + + @Test + void compoundExpression() { + Payload payload = new Payload(); + expression = parser.parseExpression("DR[0]"); + assertThat(expression.getValue(payload).toString()).isEqualTo("instanceof Two"); + assertCanCompile(expression); + assertThat(expression.getValue(payload).toString()).isEqualTo("instanceof Two"); + ast = getAst(); + assertThat(ast.getExitDescriptor()).isEqualTo("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Two"); + + expression = parser.parseExpression("holder.three"); + assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Three"); + assertCanCompile(expression); + assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Three"); + ast = getAst(); + assertThat(ast.getExitDescriptor()).isEqualTo("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Three"); + + expression = parser.parseExpression("DR[0]"); + assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Two"); + assertCanCompile(expression); + assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Two"); + assertThat(getAst().getExitDescriptor()).isEqualTo("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Two"); + + expression = parser.parseExpression("DR[0].three"); + assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Three"); + assertCanCompile(expression); + assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Three"); + ast = getAst(); + assertThat(ast.getExitDescriptor()).isEqualTo("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Three"); + + expression = parser.parseExpression("DR[0].three.four"); + assertThat(expression.getValue(payload)).isEqualTo(0.04d); + assertCanCompile(expression); + assertThat(expression.getValue(payload)).isEqualTo(0.04d); + assertThat(getAst().getExitDescriptor()).isEqualTo("D"); + } + + @Test + void compiledExpressionShouldWorkWhenUsingCustomFunctionWithVarargs() throws Exception { + StandardEvaluationContext context; + + // single string argument + expression = parser.parseExpression("#doFormat('hey %s', 'there')"); + context = new StandardEvaluationContext(); + context.registerFunction("doFormat", + DelegatingStringFormat.class.getDeclaredMethod("format", String.class, Object[].class)); + ((SpelExpression) expression).setEvaluationContext(context); + + assertThat(expression.getValue(String.class)).isEqualTo("hey there"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("hey there"); + + // single string argument from root array access + expression = parser.parseExpression("#doFormat([0], 'there')"); + context = new StandardEvaluationContext(new Object[] {"hey %s"}); + context.registerFunction("doFormat", + DelegatingStringFormat.class.getDeclaredMethod("format", String.class, Object[].class)); + ((SpelExpression) expression).setEvaluationContext(context); + + assertThat(expression.getValue(String.class)).isEqualTo("hey there"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("hey there"); + + // single string from variable + expression = parser.parseExpression("#doFormat([0], #arg)"); + context = new StandardEvaluationContext(new Object[] {"hey %s"}); + context.registerFunction("doFormat", + DelegatingStringFormat.class.getDeclaredMethod("format", String.class, Object[].class)); + context.setVariable("arg", "there"); + ((SpelExpression) expression).setEvaluationContext(context); + + assertThat(expression.getValue(String.class)).isEqualTo("hey there"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("hey there"); + + // string array argument + expression = parser.parseExpression("#doFormat('hey %s', #arg)"); + context = new StandardEvaluationContext(); + context.registerFunction("doFormat", + DelegatingStringFormat.class.getDeclaredMethod("format", String.class, Object[].class)); + context.setVariable("arg", new String[] { "there" }); + ((SpelExpression) expression).setEvaluationContext(context); + assertThat(expression.getValue(String.class)).isEqualTo("hey there"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("hey there"); + } + + @Test + void failsWhenSettingContextForExpression_SPR12326() { + SpelExpressionParser parser = new SpelExpressionParser( + new SpelParserConfiguration(SpelCompilerMode.OFF, getClass().getClassLoader())); + Person3 person = new Person3("foo", 1); + SpelExpression expression = parser.parseRaw("#it?.age?.equals([0])"); + StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {1}); + context.setVariable("it", person); + expression.setEvaluationContext(context); + assertThat(expression.getValue(Boolean.class)).isTrue(); + // This will trigger compilation (second usage) + assertThat(expression.getValue(Boolean.class)).isTrue(); + context.setVariable("it", null); + assertThat(expression.getValue(Boolean.class)).isNull(); + + assertCanCompile(expression); + + context.setVariable("it", person); + assertThat(expression.getValue(Boolean.class)).isTrue(); + context.setVariable("it", null); + assertThat(expression.getValue(Boolean.class)).isNull(); + } + + /** + * Test variants of using T(...) and static/non-static method/property/field references. + */ + @Test + void errorHandling() { + TestClass5 tc = new TestClass5(); + + // changing target + + // from primitive array to reference type array + int[] is = new int[] {1,2,3}; + String[] strings = new String[] {"a","b","c"}; + expression = parser.parseExpression("[1]"); + assertThat(expression.getValue(is)).isEqualTo(2); + assertCanCompile(expression); + assertThat(expression.getValue(is)).isEqualTo(2); + assertThatExceptionOfType(SpelEvaluationException.class) + .isThrownBy(() -> expression.getValue(strings)) + .withCauseInstanceOf(ClassCastException.class); + SpelCompiler.revertToInterpreted(expression); + assertThat(expression.getValue(strings)).isEqualTo("b"); + assertCanCompile(expression); + assertThat(expression.getValue(strings)).isEqualTo("b"); + + tc.field = "foo"; + expression = parser.parseExpression("seven(field)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("foo"); + assertCanCompile(expression); + tc.reset(); + tc.field="bar"; + expression.getValue(tc); + + // method with changing parameter types (change reference type) + tc.obj = "foo"; + expression = parser.parseExpression("seven(obj)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("foo"); + assertCanCompile(expression); + tc.reset(); + tc.obj=42; + assertThatExceptionOfType(SpelEvaluationException.class) + .isThrownBy(() -> expression.getValue(tc)) + .withCauseInstanceOf(ClassCastException.class); + + // method with changing target + expression = parser.parseExpression("#root.charAt(0)"); + assertThat(expression.getValue("abc")).isEqualTo('a'); + assertCanCompile(expression); + assertThatExceptionOfType(SpelEvaluationException.class) + .isThrownBy(() -> expression.getValue(42)) + .withCauseInstanceOf(ClassCastException.class); + } + + @Test + void mixingItUp_indexerOpEqTernary() { + Map m = new HashMap<>(); + m.put("andy","778"); + + expression = parse("['andy']==null?1:2"); + assertThat(expression.getValue(m)).isEqualTo(2); + assertCanCompile(expression); + assertThat(expression.getValue(m)).isEqualTo(2); + m.remove("andy"); + assertThat(expression.getValue(m)).isEqualTo(1); + } + + @Test + void plusNeedingCheckcast_SPR12426() { + expression = parser.parseExpression("object + ' world'"); + Object v = expression.getValue(new FooObject()); + assertThat(v).isEqualTo("hello world"); + assertCanCompile(expression); + assertThat(v).isEqualTo("hello world"); + + expression = parser.parseExpression("object + ' world'"); + v = expression.getValue(new FooString()); + assertThat(v).isEqualTo("hello world"); + assertCanCompile(expression); + assertThat(v).isEqualTo("hello world"); + } + + @Test + void mixingItUp_propertyAccessIndexerOpLtTernaryRootNull() { + Payload payload = new Payload(); + + expression = parser.parseExpression("DR[0].three"); + Object v = expression.getValue(payload); + assertThat(getAst().getExitDescriptor()).isEqualTo("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Three"); + + Expression expression = parser.parseExpression("DR[0].three.four lt 0.1d?#root:null"); + v = expression.getValue(payload); + + SpelExpression sExpr = (SpelExpression) expression; + Ternary ternary = (Ternary) sExpr.getAST(); + OpLT oplt = (OpLT) ternary.getChild(0); + CompoundExpression cExpr = (CompoundExpression) oplt.getLeftOperand(); + String cExprExitDescriptor = cExpr.getExitDescriptor(); + assertThat(cExprExitDescriptor).isEqualTo("D"); + assertThat(oplt.getExitDescriptor()).isEqualTo("Z"); + + assertCanCompile(expression); + Object vc = expression.getValue(payload); + assertThat(v).isEqualTo(payload); + assertThat(vc).isEqualTo(payload); + payload.DR[0].three.four = 0.13d; + vc = expression.getValue(payload); + assertThat(vc).isNull(); + } + + @Test + void variantGetter() { + Payload2Holder holder = new Payload2Holder(); + StandardEvaluationContext ctx = new StandardEvaluationContext(); + ctx.addPropertyAccessor(new MyPropertyAccessor()); + expression = parser.parseExpression("payload2.var1"); + Object v = expression.getValue(ctx,holder); + assertThat(v).isEqualTo("abc"); + + assertCanCompile(expression); + v = expression.getValue(ctx,holder); + assertThat(v).isEqualTo("abc"); + } + + @Test + void compilerWithGenerics_12040() { + expression = parser.parseExpression("payload!=2"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class)).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class)).isFalse(); + + expression = parser.parseExpression("2!=payload"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class)).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class)).isFalse(); + + expression = parser.parseExpression("payload!=6L"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4L), Boolean.class)).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(6L), Boolean.class)).isFalse(); + + expression = parser.parseExpression("payload==2"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class)).isFalse(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class)).isTrue(); + + expression = parser.parseExpression("2==payload"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class)).isFalse(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class)).isTrue(); + + expression = parser.parseExpression("payload==6L"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4L), Boolean.class)).isFalse(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(6L), Boolean.class)).isTrue(); + + expression = parser.parseExpression("2==payload"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class)).isFalse(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class)).isTrue(); + + expression = parser.parseExpression("payload/2"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(2); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(6))).isEqualTo(3); + + expression = parser.parseExpression("100/payload"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(25); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(10))).isEqualTo(10); + + expression = parser.parseExpression("payload+2"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(6); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(6))).isEqualTo(8); + + expression = parser.parseExpression("100+payload"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(104); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(10))).isEqualTo(110); + + expression = parser.parseExpression("payload-2"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(2); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(6))).isEqualTo(4); + + expression = parser.parseExpression("100-payload"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(96); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(10))).isEqualTo(90); + + expression = parser.parseExpression("payload*2"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(8); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(6))).isEqualTo(12); + + expression = parser.parseExpression("100*payload"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(400); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(10))).isEqualTo(1000); + + expression = parser.parseExpression("payload/2L"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4L))).isEqualTo(2L); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(6L))).isEqualTo(3L); + + expression = parser.parseExpression("100L/payload"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4L))).isEqualTo(25L); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(10L))).isEqualTo(10L); + + expression = parser.parseExpression("payload/2f"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4f))).isEqualTo(2f); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(6f))).isEqualTo(3f); + + expression = parser.parseExpression("100f/payload"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4f))).isEqualTo(25f); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(10f))).isEqualTo(10f); + + expression = parser.parseExpression("payload/2d"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4d))).isEqualTo(2d); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(6d))).isEqualTo(3d); + + expression = parser.parseExpression("100d/payload"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4d))).isEqualTo(25d); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(10d))).isEqualTo(10d); + } + + // The new helper class here uses an upper bound on the generic + @Test + void compilerWithGenerics_12040_2() { + expression = parser.parseExpression("payload/2"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(2); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(6))).isEqualTo(3); + + expression = parser.parseExpression("9/payload"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(9))).isEqualTo(1); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(3))).isEqualTo(3); + + expression = parser.parseExpression("payload+2"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(6); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(6))).isEqualTo(8); + + expression = parser.parseExpression("100+payload"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(104); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(10))).isEqualTo(110); + + expression = parser.parseExpression("payload-2"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(2); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(6))).isEqualTo(4); + + expression = parser.parseExpression("100-payload"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(96); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(10))).isEqualTo(90); + + expression = parser.parseExpression("payload*2"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(8); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(6))).isEqualTo(12); + + expression = parser.parseExpression("100*payload"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(400); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(10))).isEqualTo(1000); + } + + // The other numeric operators + @Test + void compilerWithGenerics_12040_3() { + expression = parser.parseExpression("payload >= 2"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(4), boolean.class)).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isFalse(); + + expression = parser.parseExpression("2 >= payload"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(5), boolean.class)).isFalse(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isTrue(); + + expression = parser.parseExpression("payload > 2"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(4), boolean.class)).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isFalse(); + + expression = parser.parseExpression("2 > payload"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(5), boolean.class)).isFalse(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isTrue(); + + expression = parser.parseExpression("payload <=2"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(6), boolean.class)).isFalse(); + + expression = parser.parseExpression("2 <= payload"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isFalse(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(6), boolean.class)).isTrue(); + + expression = parser.parseExpression("payload < 2"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(6), boolean.class)).isFalse(); + + expression = parser.parseExpression("2 < payload"); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isFalse(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper2<>(6), boolean.class)).isTrue(); + } + + @Test + void repeatedCompilation() throws Exception { + // Verifying that after a number of compilations, the classloaders + // used to load the compiled expressions are discarded/replaced. + // See SpelCompiler.loadClass() + Field f = SpelExpression.class.getDeclaredField("compiledAst"); + Set classloadersUsed = new HashSet<>(); + for (int i = 0; i < 1500; i++) { // 1500 is greater than SpelCompiler.CLASSES_DEFINED_LIMIT + expression = parser.parseExpression("4 + 5"); + assertThat((int) expression.getValue(Integer.class)).isEqualTo(9); + assertCanCompile(expression); + f.setAccessible(true); + CompiledExpression cEx = (CompiledExpression) f.get(expression); + classloadersUsed.add(cEx.getClass().getClassLoader()); + assertThat((int) expression.getValue(Integer.class)).isEqualTo(9); + } + assertThat(classloadersUsed.size()).isGreaterThan(1); + } + } + + @Nested + class RelationalOperatorTests { + + @SuppressWarnings("unchecked") + @Test + void operatorInstanceOf() { + expression = parse("'xyz' instanceof T(String)"); + assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); + + expression = parse("'xyz' instanceof T(Integer)"); + assertThat(expression.getValue()).isEqualTo(false); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(false); + + List list = new ArrayList<>(); + expression = parse("#root instanceof T(java.util.List)"); + assertThat(expression.getValue(list)).asInstanceOf(BOOLEAN).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(list)).asInstanceOf(BOOLEAN).isTrue(); + + List[] arrayOfLists = new List[] {new ArrayList()}; + expression = parse("#root instanceof T(java.util.List[])"); + assertThat(expression.getValue(arrayOfLists)).asInstanceOf(BOOLEAN).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(arrayOfLists)).asInstanceOf(BOOLEAN).isTrue(); + + int[] intArray = new int[] {1,2,3}; + expression = parse("#root instanceof T(int[])"); + assertThat(expression.getValue(intArray)).asInstanceOf(BOOLEAN).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(intArray)).asInstanceOf(BOOLEAN).isTrue(); + + String root = null; + expression = parse("#root instanceof T(Integer)"); + assertThat(expression.getValue(root)).isEqualTo(false); + assertCanCompile(expression); + assertThat(expression.getValue(root)).isEqualTo(false); + + // root still null + expression = parse("#root instanceof T(java.lang.Object)"); + assertThat(expression.getValue(root)).isEqualTo(false); + assertCanCompile(expression); + assertThat(expression.getValue(root)).isEqualTo(false); + + root = "howdy!"; + expression = parse("#root instanceof T(java.lang.Object)"); + assertThat(expression.getValue(root)).asInstanceOf(BOOLEAN).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(root)).asInstanceOf(BOOLEAN).isTrue(); + } + + @Test + void operatorInstanceOf_SPR14250() { + // primitive left operand - should get boxed, return true + expression = parse("3 instanceof T(Integer)"); + assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); + + // primitive left operand - should get boxed, return false + expression = parse("3 instanceof T(String)"); + assertThat(expression.getValue()).isEqualTo(false); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(false); + + // double slot left operand - should get boxed, return false + expression = parse("3.0d instanceof T(Integer)"); + assertThat(expression.getValue()).isEqualTo(false); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(false); + + // double slot left operand - should get boxed, return true + expression = parse("3.0d instanceof T(Double)"); + assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); + + // Only when the right-hand operand is a direct type reference + // will it be compilable. + StandardEvaluationContext ctx = new StandardEvaluationContext(); + ctx.setVariable("foo", String.class); + expression = parse("3 instanceof #foo"); + assertThat(expression.getValue(ctx)).isEqualTo(false); + assertCannotCompile(expression); + + // use of primitive as type for instanceof check - compilable + // but always false + expression = parse("3 instanceof T(int)"); + assertThat(expression.getValue()).isEqualTo(false); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(false); + + expression = parse("3 instanceof T(long)"); + assertThat(expression.getValue()).isEqualTo(false); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(false); + } + + @Test + void opLt() { + expression = parse("3.0d < 4.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("3446.0d < 1123.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("3 < 1"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("2 < 4"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("3.0f < 1.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("1.0f < 5.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("30L < 30L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("15L < 20L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + // Differing types of number, not yet supported + expression = parse("1 < 3.0d"); + assertCannotCompile(expression); + + expression = parse("T(Integer).valueOf(3) < 4"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("T(Integer).valueOf(3) < T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("5 < T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + } + + @Test + void opLe() { + expression = parse("3.0d <= 4.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("3446.0d <= 1123.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("3446.0d <= 3446.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("3 <= 1"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("2 <= 4"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("3 <= 3"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("3.0f <= 1.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("1.0f <= 5.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("2.0f <= 2.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("30L <= 30L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("15L <= 20L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + // Differing types of number, not yet supported + expression = parse("1 <= 3.0d"); + assertCannotCompile(expression); + + expression = parse("T(Integer).valueOf(3) <= 4"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("T(Integer).valueOf(3) <= T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("5 <= T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + } + + @Test + void opGt() { + expression = parse("3.0d > 4.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("3446.0d > 1123.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("3 > 1"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("2 > 4"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("3.0f > 1.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("1.0f > 5.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("30L > 30L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("15L > 20L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + // Differing types of number, not yet supported + expression = parse("1 > 3.0d"); + assertCannotCompile(expression); + + expression = parse("T(Integer).valueOf(3) > 4"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Integer).valueOf(3) > T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("5 > T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + } + + @Test + void opGe() { + expression = parse("3.0d >= 4.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("3446.0d >= 1123.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("3446.0d >= 3446.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("3 >= 1"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("2 >= 4"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("3 >= 3"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("3.0f >= 1.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("1.0f >= 5.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("3.0f >= 3.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("40L >= 30L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("15L >= 20L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("30L >= 30L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + // Differing types of number, not yet supported + expression = parse("1 >= 3.0d"); + assertCannotCompile(expression); + + expression = parse("T(Integer).valueOf(3) >= 4"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Integer).valueOf(3) >= T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("5 >= T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + } + + @Test + void opEq() { + String tvar = "35"; + expression = parse("#root == 35"); + assertThat((Boolean) expression.getValue(tvar)).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue(tvar)).isFalse(); + + expression = parse("35 == #root"); + expression.getValue(tvar); + assertThat((Boolean) expression.getValue(tvar)).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue(tvar)).isFalse(); + + TestClass7 tc7 = new TestClass7(); + TestClass7.reset(); + expression = parse("property == 'UK'"); + assertThat((Boolean) expression.getValue(tc7)).isTrue(); + TestClass7.property = null; + assertThat((Boolean) expression.getValue(tc7)).isFalse(); + assertCanCompile(expression); + TestClass7.reset(); + assertThat((Boolean) expression.getValue(tc7)).isTrue(); + TestClass7.property = "UK"; + assertThat((Boolean) expression.getValue(tc7)).isTrue(); + TestClass7.reset(); + TestClass7.property = null; + assertThat((Boolean) expression.getValue(tc7)).isFalse(); + expression = parse("property == null"); + assertThat((Boolean) expression.getValue(tc7)).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue(tc7)).isTrue(); + + expression = parse("3.0d == 4.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("3446.0d == 3446.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("3 == 1"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("3 == 3"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("3.0f == 1.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("2.0f == 2.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("30L == 30L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("15L == 20L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + // number types are not the same + expression = parse("1 == 3.0d"); + assertCannotCompile(expression); + + Double d = 3.0d; + expression = parse("#root==3.0d"); + assertThat((Boolean) expression.getValue(d)).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue(d)).isTrue(); + + Integer i = 3; + expression = parse("#root==3"); + assertThat((Boolean) expression.getValue(i)).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue(i)).isTrue(); + + Float f = 3.0f; + expression = parse("#root==3.0f"); + assertThat((Boolean) expression.getValue(f)).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue(f)).isTrue(); + + long l = 300L; + expression = parse("#root==300l"); + assertThat((Boolean) expression.getValue(l)).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue(l)).isTrue(); + + boolean b = true; + expression = parse("#root==true"); + assertThat((Boolean) expression.getValue(b)).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue(b)).isTrue(); + + expression = parse("T(Integer).valueOf(3) == 4"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Integer).valueOf(3) == T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("5 == T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Float).valueOf(3.0f) == 4.0f"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Float).valueOf(3.0f) == T(Float).valueOf(3.0f)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("5.0f == T(Float).valueOf(3.0f)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Long).valueOf(3L) == 4L"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Long).valueOf(3L) == T(Long).valueOf(3L)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("5L == T(Long).valueOf(3L)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Double).valueOf(3.0d) == 4.0d"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Double).valueOf(3.0d) == T(Double).valueOf(3.0d)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("5.0d == T(Double).valueOf(3.0d)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("false == true"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Boolean).valueOf('true') == T(Boolean).valueOf('true')"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("T(Boolean).valueOf('true') == true"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("false == T(Boolean).valueOf('false')"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + } + + @Test + void opNe() { + expression = parse("3.0d != 4.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("3446.0d != 3446.0d"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("3 != 1"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("3 != 3"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("3.0f != 1.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + expression = parse("2.0f != 2.0f"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("30L != 30L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + expression = parse("15L != 20L"); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + // not compatible number types + expression = parse("1 != 3.0d"); + assertCannotCompile(expression); + + expression = parse("T(Integer).valueOf(3) != 4"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("T(Integer).valueOf(3) != T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("5 != T(Integer).valueOf(3)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("T(Float).valueOf(3.0f) != 4.0f"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("T(Float).valueOf(3.0f) != T(Float).valueOf(3.0f)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("5.0f != T(Float).valueOf(3.0f)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("T(Long).valueOf(3L) != 4L"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("T(Long).valueOf(3L) != T(Long).valueOf(3L)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("5L != T(Long).valueOf(3L)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("T(Double).valueOf(3.0d) == 4.0d"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Double).valueOf(3.0d) == T(Double).valueOf(3.0d)"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("5.0d == T(Double).valueOf(3.0d)"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("false == true"); + assertThat((Boolean) expression.getValue()).isFalse(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isFalse(); + + expression = parse("T(Boolean).valueOf('true') == T(Boolean).valueOf('true')"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("T(Boolean).valueOf('true') == true"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + + expression = parse("false == T(Boolean).valueOf('false')"); + assertThat((Boolean) expression.getValue()).isTrue(); + assertCanCompile(expression); + assertThat((Boolean) expression.getValue()).isTrue(); + } + + @Test + void opNe_SPR14863() { + SpelParserConfiguration configuration = + new SpelParserConfiguration(SpelCompilerMode.MIXED, ClassLoader.getSystemClassLoader()); + SpelExpressionParser parser = new SpelExpressionParser(configuration); + Expression expression = parser.parseExpression("data['my-key'] != 'my-value'"); + + Map data = new HashMap<>(); + data.put("my-key", "my-value"); + StandardEvaluationContext context = new StandardEvaluationContext(new MyContext(data)); + assertThat(expression.getValue(context, Boolean.class)).isFalse(); + assertCanCompile(expression); + ((SpelExpression) expression).compileExpression(); + assertThat(expression.getValue(context, Boolean.class)).isFalse(); + + List ls = new ArrayList<>(); + ls.add("foo"); + context = new StandardEvaluationContext(ls); + expression = parse("get(0) != 'foo'"); + assertThat(expression.getValue(context, Boolean.class)).isFalse(); + assertCanCompile(expression); + assertThat(expression.getValue(context, Boolean.class)).isFalse(); + + ls.remove(0); + ls.add("goo"); + assertThat(expression.getValue(context, Boolean.class)).isTrue(); + } + + @Test + void opEq_SPR14863() { + // Exercise the comparator invocation code that runs in + // equalityCheck() (called from interpreted and compiled code) + expression = parser.parseExpression("#aa==#bb"); + StandardEvaluationContext sec = new StandardEvaluationContext(); + Apple aa = new Apple(1); + Apple bb = new Apple(2); + sec.setVariable("aa",aa); + sec.setVariable("bb",bb); + boolean b = expression.getValue(sec, Boolean.class); + // Verify what the expression caused aa to be compared to + assertThat(aa.gotComparedTo).isEqualTo(bb); + assertThat(b).isFalse(); + bb.setValue(1); + b = expression.getValue(sec, Boolean.class); + assertThat(aa.gotComparedTo).isEqualTo(bb); + assertThat(b).isTrue(); + + assertCanCompile(expression); + + // Similar test with compiled expression + aa = new Apple(99); + bb = new Apple(100); + sec.setVariable("aa",aa); + sec.setVariable("bb",bb); + b = expression.getValue(sec, Boolean.class); + assertThat(b).isFalse(); + assertThat(aa.gotComparedTo).isEqualTo(bb); + bb.setValue(99); + b = expression.getValue(sec, Boolean.class); + assertThat(b).isTrue(); + assertThat(aa.gotComparedTo).isEqualTo(bb); + + List ls = new ArrayList<>(); + ls.add("foo"); + StandardEvaluationContext context = new StandardEvaluationContext(ls); + expression = parse("get(0) == 'foo'"); + assertThat(expression.getValue(context, Boolean.class)).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context, Boolean.class)).isTrue(); + + ls.remove(0); + ls.add("goo"); + assertThat(expression.getValue(context, Boolean.class)).isFalse(); + } + + @Test + void nullComparison_SPR22358() { + SpelParserConfiguration configuration = new SpelParserConfiguration(SpelCompilerMode.OFF, null); + SpelExpressionParser parser = new SpelExpressionParser(configuration); + StandardEvaluationContext ctx = new StandardEvaluationContext(); + ctx.setRootObject(new Reg(1)); + verifyCompilationAndBehaviourWithNull("value>1", parser, ctx ); + verifyCompilationAndBehaviourWithNull("value<1", parser, ctx ); + verifyCompilationAndBehaviourWithNull("value>=1", parser, ctx ); + verifyCompilationAndBehaviourWithNull("value<=1", parser, ctx ); + + verifyCompilationAndBehaviourWithNull2("value>value2", parser, ctx ); + verifyCompilationAndBehaviourWithNull2("value=value2", parser, ctx ); + verifyCompilationAndBehaviourWithNull2("value<=value2", parser, ctx ); + + verifyCompilationAndBehaviourWithNull("valueD>1.0d", parser, ctx ); + verifyCompilationAndBehaviourWithNull("valueD<1.0d", parser, ctx ); + verifyCompilationAndBehaviourWithNull("valueD>=1.0d", parser, ctx ); + verifyCompilationAndBehaviourWithNull("valueD<=1.0d", parser, ctx ); + + verifyCompilationAndBehaviourWithNull2("valueD>valueD2", parser, ctx ); + verifyCompilationAndBehaviourWithNull2("valueD=valueD2", parser, ctx ); + verifyCompilationAndBehaviourWithNull2("valueD<=valueD2", parser, ctx ); + + verifyCompilationAndBehaviourWithNull("valueL>1L", parser, ctx ); + verifyCompilationAndBehaviourWithNull("valueL<1L", parser, ctx ); + verifyCompilationAndBehaviourWithNull("valueL>=1L", parser, ctx ); + verifyCompilationAndBehaviourWithNull("valueL<=1L", parser, ctx ); + + verifyCompilationAndBehaviourWithNull2("valueL>valueL2", parser, ctx ); + verifyCompilationAndBehaviourWithNull2("valueL=valueL2", parser, ctx ); + verifyCompilationAndBehaviourWithNull2("valueL<=valueL2", parser, ctx ); + + verifyCompilationAndBehaviourWithNull("valueF>1.0f", parser, ctx ); + verifyCompilationAndBehaviourWithNull("valueF<1.0f", parser, ctx ); + verifyCompilationAndBehaviourWithNull("valueF>=1.0f", parser, ctx ); + verifyCompilationAndBehaviourWithNull("valueF<=1.0f", parser, ctx ); + + verifyCompilationAndBehaviourWithNull("valueF>valueF2", parser, ctx ); + verifyCompilationAndBehaviourWithNull("valueF=valueF2", parser, ctx ); + verifyCompilationAndBehaviourWithNull("valueF<=valueF2", parser, ctx ); + } + + private void verifyCompilationAndBehaviourWithNull(String expressionText, SpelExpressionParser parser, StandardEvaluationContext ctx) { + Reg r = (Reg)ctx.getRootObject().getValue(); + r.setValue2(1); // having a value in value2 fields will enable compilation to succeed, then can switch it to null + SpelExpression fast = (SpelExpression) parser.parseExpression(expressionText); + SpelExpression slow = (SpelExpression) parser.parseExpression(expressionText); + fast.getValue(ctx); + assertThat(fast.compileExpression()).isTrue(); + r.setValue2(null); + // try the numbers 0,1,2,null + for (int i = 0; i < 4; i++) { + r.setValue(i < 3 ? i : null); + boolean slowResult = (Boolean)slow.getValue(ctx); + boolean fastResult = (Boolean)fast.getValue(ctx); + assertThat(fastResult).as("Differing results: expression=" + expressionText + + " value=" + r.getValue() + " slow=" + slowResult + " fast="+fastResult).isEqualTo(slowResult); + } + } + + private void verifyCompilationAndBehaviourWithNull2(String expressionText, SpelExpressionParser parser, StandardEvaluationContext ctx) { + SpelExpression fast = (SpelExpression) parser.parseExpression(expressionText); + SpelExpression slow = (SpelExpression) parser.parseExpression(expressionText); + fast.getValue(ctx); + assertThat(fast.compileExpression()).isTrue(); + Reg r = (Reg)ctx.getRootObject().getValue(); + // try the numbers 0,1,2,null + for (int i = 0; i < 4; i++) { + r.setValue(i < 3 ? i : null); + boolean slowResult = (Boolean)slow.getValue(ctx); + boolean fastResult = (Boolean)fast.getValue(ctx); + assertThat(fastResult).as("Differing results: expression=" + expressionText + + " value=" + r.getValue() + " slow=" + slowResult + " fast="+fastResult).isEqualTo(slowResult); + } + } + } + + @Nested + class LiteralTests { + + @Test + void nullLiteral() { + expression = parser.parseExpression("null"); + Object interpretedResult = expression.getValue(new TestClass1(), Object.class); + assertCanCompile(expression); + Object compiledResult = expression.getValue(new TestClass1(), Object.class); + assertThat(interpretedResult).isNull(); + assertThat(compiledResult).isNull(); + } + + @Test + void stringLiteral() { + expression = parser.parseExpression("'abcde'"); + assertThat(expression.getValue(new TestClass1(), String.class)).isEqualTo("abcde"); + assertCanCompile(expression); + String compiledResult = expression.getValue(new TestClass1(), String.class); + assertThat(compiledResult).isEqualTo("abcde"); + assertThat(expression.getValue(String.class)).isEqualTo("abcde"); + assertThat(expression.getValue()).isEqualTo("abcde"); + assertThat(expression.getValue(new StandardEvaluationContext())).isEqualTo("abcde"); + + expression = parser.parseExpression("\"abcde\""); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("abcde"); + } + + @Test + void floatLiteral() { + expression = parser.parseExpression("3.4f"); + float interpretedResult = expression.getValue(new TestClass1(), float.class); + assertCanCompile(expression); + float compiledResult = expression.getValue(new TestClass1(), float.class); + assertThat(interpretedResult).isCloseTo(3.4f, within(0.1f)); + assertThat(compiledResult).isCloseTo(3.4f, within(0.1f)); + assertThat(expression.getValue()).isEqualTo(3.4f); + } + + @Test + void realLiteral() { + expression = parser.parseExpression("3.4d"); + double interpretedResult = expression.getValue(new TestClass1(), double.class); + assertCanCompile(expression); + double compiledResult = expression.getValue(new TestClass1(), double.class); + assertThat(interpretedResult).isCloseTo(3.4d, within(0.1d)); + assertThat(compiledResult).isCloseTo(3.4d, within(0.1d)); + assertThat(expression.getValue()).isEqualTo(3.4d); + } + + @SuppressWarnings("rawtypes") + @Test + void intLiteral() { + expression = parser.parseExpression("42"); + int interpretedResult = expression.getValue(new TestClass1(), int.class); + assertCanCompile(expression); + int compiledResult = expression.getValue(new TestClass1(), int.class); + assertThat(interpretedResult).isEqualTo(42); + assertThat(compiledResult).isEqualTo(42); + + expression = parser.parseExpression("T(Integer).valueOf(42)"); + expression.getValue(Integer.class); + assertCanCompile(expression); + assertThat(expression.getValue(Integer.class)).isEqualTo(42); + + // Code gen is different for -1 .. 6 because there are bytecode instructions specifically for those values + + // Not an int literal but an opMinus with one operand: + expression = parser.parseExpression("-1"); + expression.getValue(Integer.class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(-1); + + expression = parser.parseExpression("0"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(0); + + expression = parser.parseExpression("2"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(2); + + expression = parser.parseExpression("7"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(7); + } + + @Test + void longLiteral() { + expression = parser.parseExpression("99L"); + long interpretedResult = expression.getValue(new TestClass1(), long.class); + assertCanCompile(expression); + long compiledResult = expression.getValue(new TestClass1(), long.class); + assertThat(interpretedResult).isEqualTo(99L); + assertThat(compiledResult).isEqualTo(99L); + } + + @Test + void booleanLiteral() { + expression = parser.parseExpression("true"); + boolean interpretedResult = expression.getValue(1, boolean.class); + assertThat(interpretedResult).isTrue(); + assertThat(SpelCompiler.compile(expression)).isTrue(); + boolean compiledResult = expression.getValue(1, boolean.class); + assertThat(compiledResult).isTrue(); + + expression = parser.parseExpression("false"); + interpretedResult = expression.getValue(1, boolean.class); + assertThat(interpretedResult).isFalse(); + assertThat(SpelCompiler.compile(expression)).isTrue(); + compiledResult = expression.getValue(1, boolean.class); + assertThat(compiledResult).isFalse(); + } + } + + @Nested + class InlineListTests { + + @SuppressWarnings("rawtypes") + @Test + void inlineList() { + expression = parser.parseExpression("'abcde'.substring({1,3,4}[0])"); + Object o = expression.getValue(); + assertThat(o).isEqualTo("bcde"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo("bcde"); + + expression = parser.parseExpression("{'abc','def'}"); + List l = (List) expression.getValue(); + assertThat(l.toString()).isEqualTo("[abc, def]"); + assertCanCompile(expression); + l = (List) expression.getValue(); + assertThat(l.toString()).isEqualTo("[abc, def]"); + + expression = parser.parseExpression("{'abc','def'}[0]"); + o = expression.getValue(); + assertThat(o).isEqualTo("abc"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo("abc"); + + expression = parser.parseExpression("{'abcde','ijklm'}[0].substring({1,3,4}[0])"); + o = expression.getValue(); + assertThat(o).isEqualTo("bcde"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo("bcde"); + + expression = parser.parseExpression("{'abcde','ijklm'}[0].substring({1,3,4}[0],{1,3,4}[1])"); + o = expression.getValue(); + assertThat(o).isEqualTo("bc"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo("bc"); + } + + @SuppressWarnings("rawtypes") + @Test + void nestedInlineLists() { + Object o = null; + + expression = parser.parseExpression("{{1,2,3},{4,5,6},{7,8,9}}"); + o = expression.getValue(); + assertThat(o.toString()).isEqualTo("[[1, 2, 3], [4, 5, 6], [7, 8, 9]]"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o.toString()).isEqualTo("[[1, 2, 3], [4, 5, 6], [7, 8, 9]]"); + + expression = parser.parseExpression("{{1,2,3},{4,5,6},{7,8,9}}.toString()"); + o = expression.getValue(); + assertThat(o).isEqualTo("[[1, 2, 3], [4, 5, 6], [7, 8, 9]]"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo("[[1, 2, 3], [4, 5, 6], [7, 8, 9]]"); + + expression = parser.parseExpression("{{1,2,3},{4,5,6},{7,8,9}}[1][0]"); + o = expression.getValue(); + assertThat(o).isEqualTo(4); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo(4); + + expression = parser.parseExpression("{{1,2,3},'abc',{7,8,9}}[1]"); + o = expression.getValue(); + assertThat(o).isEqualTo("abc"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo("abc"); + + expression = parser.parseExpression("'abcde'.substring({{1,3},1,3,4}[0][1])"); + o = expression.getValue(); + assertThat(o).isEqualTo("de"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo("de"); + + expression = parser.parseExpression("'abcde'.substring({{1,3},1,3,4}[1])"); + o = expression.getValue(); + assertThat(o).isEqualTo("bcde"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo("bcde"); + + expression = parser.parseExpression("{'abc',{'def','ghi'}}"); + List l = (List) expression.getValue(); + assertThat(l.toString()).isEqualTo("[abc, [def, ghi]]"); + assertCanCompile(expression); + l = (List) expression.getValue(); + assertThat(l.toString()).isEqualTo("[abc, [def, ghi]]"); + + expression = parser.parseExpression("{'abcde',{'ijklm','nopqr'}}[0].substring({1,3,4}[0])"); + o = expression.getValue(); + assertThat(o).isEqualTo("bcde"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo("bcde"); + + expression = parser.parseExpression("{'abcde',{'ijklm','nopqr'}}[1][0].substring({1,3,4}[0])"); + o = expression.getValue(); + assertThat(o).isEqualTo("jklm"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo("jklm"); + + expression = parser.parseExpression("{'abcde',{'ijklm','nopqr'}}[1][1].substring({1,3,4}[0],{1,3,4}[1])"); + o = expression.getValue(); + assertThat(o).isEqualTo("op"); + assertCanCompile(expression); + o = expression.getValue(); + assertThat(o).isEqualTo("op"); + } + } + + @Nested + class UnaryOperatorTests { + + @Test + void operatorNot() { + expression = parse("!true"); + assertThat(expression.getValue()).isEqualTo(false); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(false); + + expression = parse("!false"); + assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); + + boolean b = true; + expression = parse("!#root"); + assertThat(expression.getValue(b)).isEqualTo(false); + assertCanCompile(expression); + assertThat(expression.getValue(b)).isEqualTo(false); + + b = false; + expression = parse("!#root"); + assertThat(expression.getValue(b)).asInstanceOf(BOOLEAN).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(b)).asInstanceOf(BOOLEAN).isTrue(); + } + } + + @Nested + class BinaryOperatorTests { + + @Test + void opOr() { + Expression expression = parser.parseExpression("false or false"); + boolean interpretedResult = expression.getValue(1, boolean.class); + SpelCompiler.compile(expression); + boolean compiledResult = expression.getValue(1, boolean.class); + assertThat(interpretedResult).isFalse(); + assertThat(compiledResult).isFalse(); + + expression = parser.parseExpression("false or true"); + interpretedResult = expression.getValue(1, boolean.class); + assertCanCompile(expression); + compiledResult = expression.getValue(1, boolean.class); + assertThat(interpretedResult).isTrue(); + assertThat(compiledResult).isTrue(); + + expression = parser.parseExpression("true or false"); + interpretedResult = expression.getValue(1, boolean.class); + assertCanCompile(expression); + compiledResult = expression.getValue(1, boolean.class); + assertThat(interpretedResult).isTrue(); + assertThat(compiledResult).isTrue(); + + expression = parser.parseExpression("true or true"); + interpretedResult = expression.getValue(1, boolean.class); + assertCanCompile(expression); + compiledResult = expression.getValue(1, boolean.class); + assertThat(interpretedResult).isTrue(); + assertThat(compiledResult).isTrue(); + + TestClass4 tc = new TestClass4(); + expression = parser.parseExpression("getfalse() or gettrue()"); + interpretedResult = expression.getValue(tc, boolean.class); + assertCanCompile(expression); + compiledResult = expression.getValue(tc, boolean.class); + assertThat(interpretedResult).isTrue(); + assertThat(compiledResult).isTrue(); + + // Can't compile this as we aren't going down the getfalse() branch in our evaluation + expression = parser.parseExpression("gettrue() or getfalse()"); + interpretedResult = expression.getValue(tc, boolean.class); + assertCannotCompile(expression); + + expression = parser.parseExpression("getA() or getB()"); + tc.a = true; + tc.b = true; + interpretedResult = expression.getValue(tc, boolean.class); + assertCannotCompile(expression); // Haven't yet been into second branch + tc.a = false; + tc.b = true; + interpretedResult = expression.getValue(tc, boolean.class); + assertCanCompile(expression); // Now been down both + assertThat(interpretedResult).isTrue(); + + boolean b = false; + expression = parse("#root or #root"); + Object interpretedResult2 = expression.getValue(b); + assertCanCompile(expression); + assertThat((Boolean) interpretedResult2).isFalse(); + assertThat((Boolean) expression.getValue(b)).isFalse(); + } + + @Test + void opAnd() { + Expression expression = parser.parseExpression("false and false"); + boolean interpretedResult = expression.getValue(1, boolean.class); + SpelCompiler.compile(expression); + boolean compiledResult = expression.getValue(1, boolean.class); + assertThat(interpretedResult).isFalse(); + assertThat(compiledResult).isFalse(); + + expression = parser.parseExpression("false and true"); + interpretedResult = expression.getValue(1, boolean.class); + SpelCompiler.compile(expression); + compiledResult = expression.getValue(1, boolean.class); + assertThat(interpretedResult).isFalse(); + assertThat(compiledResult).isFalse(); + + expression = parser.parseExpression("true and false"); + interpretedResult = expression.getValue(1, boolean.class); + SpelCompiler.compile(expression); + compiledResult = expression.getValue(1, boolean.class); + assertThat(interpretedResult).isFalse(); + assertThat(compiledResult).isFalse(); + + expression = parser.parseExpression("true and true"); + interpretedResult = expression.getValue(1, boolean.class); + SpelCompiler.compile(expression); + compiledResult = expression.getValue(1, boolean.class); + assertThat(interpretedResult).isTrue(); + assertThat(compiledResult).isTrue(); + + TestClass4 tc = new TestClass4(); + + // Can't compile this as we aren't going down the gettrue() branch in our evaluation + expression = parser.parseExpression("getfalse() and gettrue()"); + interpretedResult = expression.getValue(tc, boolean.class); + assertCannotCompile(expression); + + expression = parser.parseExpression("getA() and getB()"); + tc.a = false; + tc.b = false; + interpretedResult = expression.getValue(tc, boolean.class); + assertCannotCompile(expression); // Haven't yet been into second branch + tc.a = true; + tc.b = false; + interpretedResult = expression.getValue(tc, boolean.class); + assertCanCompile(expression); // Now been down both + assertThat(interpretedResult).isFalse(); + tc.a = true; + tc.b = true; + interpretedResult = expression.getValue(tc, boolean.class); + assertThat(interpretedResult).isTrue(); + + boolean b = true; + expression = parse("#root and #root"); + Object interpretedResult2 = expression.getValue(b); + assertCanCompile(expression); + assertThat((Boolean) interpretedResult2).isTrue(); + assertThat((Boolean) expression.getValue(b)).isTrue(); + } + } + + @Nested + class ArithmeticOperatorTests { + + @Test + void opPlus() { + expression = parse("2+2"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(4); + + expression = parse("2L+2L"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(4L); + + expression = parse("2.0f+2.0f"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(4.0f); + + expression = parse("3.0d+4.0d"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(7.0d); + + expression = parse("+1"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1); + + expression = parse("+1L"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1L); + + expression = parse("+1.5f"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1.5f); + + expression = parse("+2.5d"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(2.5d); + + expression = parse("+T(Double).valueOf(2.5d)"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(2.5d); + + expression = parse("T(Integer).valueOf(2)+6"); + assertThat(expression.getValue()).isEqualTo(8); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(8); + + expression = parse("T(Integer).valueOf(1)+T(Integer).valueOf(3)"); + assertThat(expression.getValue()).isEqualTo(4); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(4); + + expression = parse("1+T(Integer).valueOf(3)"); + assertThat(expression.getValue()).isEqualTo(4); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(4); + + expression = parse("T(Float).valueOf(2.0f)+6"); + assertThat(expression.getValue()).isEqualTo(8.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(8.0f); + + expression = parse("T(Float).valueOf(2.0f)+T(Float).valueOf(3.0f)"); + assertThat(expression.getValue()).isEqualTo(5.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(5.0f); + + expression = parse("3L+T(Long).valueOf(4L)"); + assertThat(expression.getValue()).isEqualTo(7L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(7L); + + expression = parse("T(Long).valueOf(2L)+6"); + assertThat(expression.getValue()).isEqualTo(8L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(8L); + + expression = parse("T(Long).valueOf(2L)+T(Long).valueOf(3L)"); + assertThat(expression.getValue()).isEqualTo(5L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(5L); + + expression = parse("1L+T(Long).valueOf(2L)"); + assertThat(expression.getValue()).isEqualTo(3L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(3L); + } + + @Test + void opDivide_mixedNumberTypes() { + PayloadX p = new PayloadX(); + + // This is what you had to do before the changes in order for it to compile: + // expression = parse("(T(java.lang.Double).parseDouble(payload.valueI.toString()))/60D"); + + // right is a double + checkCalc(p,"payload.valueSB/60D",2d); + checkCalc(p,"payload.valueBB/60D",2d); + checkCalc(p,"payload.valueFB/60D",2d); + checkCalc(p,"payload.valueDB/60D",2d); + checkCalc(p,"payload.valueJB/60D",2d); + checkCalc(p,"payload.valueIB/60D",2d); + + checkCalc(p,"payload.valueS/60D",2d); + checkCalc(p,"payload.valueB/60D",2d); + checkCalc(p,"payload.valueF/60D",2d); + checkCalc(p,"payload.valueD/60D",2d); + checkCalc(p,"payload.valueJ/60D",2d); + checkCalc(p,"payload.valueI/60D",2d); + + checkCalc(p,"payload.valueSB/payload.valueDB60",2d); + checkCalc(p,"payload.valueBB/payload.valueDB60",2d); + checkCalc(p,"payload.valueFB/payload.valueDB60",2d); + checkCalc(p,"payload.valueDB/payload.valueDB60",2d); + checkCalc(p,"payload.valueJB/payload.valueDB60",2d); + checkCalc(p,"payload.valueIB/payload.valueDB60",2d); + + checkCalc(p,"payload.valueS/payload.valueDB60",2d); + checkCalc(p,"payload.valueB/payload.valueDB60",2d); + checkCalc(p,"payload.valueF/payload.valueDB60",2d); + checkCalc(p,"payload.valueD/payload.valueDB60",2d); + checkCalc(p,"payload.valueJ/payload.valueDB60",2d); + checkCalc(p,"payload.valueI/payload.valueDB60",2d); + + // right is a float + checkCalc(p,"payload.valueSB/60F",2F); + checkCalc(p,"payload.valueBB/60F",2F); + checkCalc(p,"payload.valueFB/60F",2f); + checkCalc(p,"payload.valueDB/60F",2d); + checkCalc(p,"payload.valueJB/60F",2F); + checkCalc(p,"payload.valueIB/60F",2F); + + checkCalc(p,"payload.valueS/60F",2F); + checkCalc(p,"payload.valueB/60F",2F); + checkCalc(p,"payload.valueF/60F",2f); + checkCalc(p,"payload.valueD/60F",2d); + checkCalc(p,"payload.valueJ/60F",2F); + checkCalc(p,"payload.valueI/60F",2F); + + checkCalc(p,"payload.valueSB/payload.valueFB60",2F); + checkCalc(p,"payload.valueBB/payload.valueFB60",2F); + checkCalc(p,"payload.valueFB/payload.valueFB60",2f); + checkCalc(p,"payload.valueDB/payload.valueFB60",2d); + checkCalc(p,"payload.valueJB/payload.valueFB60",2F); + checkCalc(p,"payload.valueIB/payload.valueFB60",2F); + + checkCalc(p,"payload.valueS/payload.valueFB60",2F); + checkCalc(p,"payload.valueB/payload.valueFB60",2F); + checkCalc(p,"payload.valueF/payload.valueFB60",2f); + checkCalc(p,"payload.valueD/payload.valueFB60",2d); + checkCalc(p,"payload.valueJ/payload.valueFB60",2F); + checkCalc(p,"payload.valueI/payload.valueFB60",2F); + + // right is a long + checkCalc(p,"payload.valueSB/60L",2L); + checkCalc(p,"payload.valueBB/60L",2L); + checkCalc(p,"payload.valueFB/60L",2f); + checkCalc(p,"payload.valueDB/60L",2d); + checkCalc(p,"payload.valueJB/60L",2L); + checkCalc(p,"payload.valueIB/60L",2L); + + checkCalc(p,"payload.valueS/60L",2L); + checkCalc(p,"payload.valueB/60L",2L); + checkCalc(p,"payload.valueF/60L",2f); + checkCalc(p,"payload.valueD/60L",2d); + checkCalc(p,"payload.valueJ/60L",2L); + checkCalc(p,"payload.valueI/60L",2L); + + checkCalc(p,"payload.valueSB/payload.valueJB60",2L); + checkCalc(p,"payload.valueBB/payload.valueJB60",2L); + checkCalc(p,"payload.valueFB/payload.valueJB60",2f); + checkCalc(p,"payload.valueDB/payload.valueJB60",2d); + checkCalc(p,"payload.valueJB/payload.valueJB60",2L); + checkCalc(p,"payload.valueIB/payload.valueJB60",2L); + + checkCalc(p,"payload.valueS/payload.valueJB60",2L); + checkCalc(p,"payload.valueB/payload.valueJB60",2L); + checkCalc(p,"payload.valueF/payload.valueJB60",2f); + checkCalc(p,"payload.valueD/payload.valueJB60",2d); + checkCalc(p,"payload.valueJ/payload.valueJB60",2L); + checkCalc(p,"payload.valueI/payload.valueJB60",2L); + + // right is an int + checkCalc(p,"payload.valueSB/60",2); + checkCalc(p,"payload.valueBB/60",2); + checkCalc(p,"payload.valueFB/60",2f); + checkCalc(p,"payload.valueDB/60",2d); + checkCalc(p,"payload.valueJB/60",2L); + checkCalc(p,"payload.valueIB/60",2); + + checkCalc(p,"payload.valueS/60",2); + checkCalc(p,"payload.valueB/60",2); + checkCalc(p,"payload.valueF/60",2f); + checkCalc(p,"payload.valueD/60",2d); + checkCalc(p,"payload.valueJ/60",2L); + checkCalc(p,"payload.valueI/60",2); + + checkCalc(p,"payload.valueSB/payload.valueIB60",2); + checkCalc(p,"payload.valueBB/payload.valueIB60",2); + checkCalc(p,"payload.valueFB/payload.valueIB60",2f); + checkCalc(p,"payload.valueDB/payload.valueIB60",2d); + checkCalc(p,"payload.valueJB/payload.valueIB60",2L); + checkCalc(p,"payload.valueIB/payload.valueIB60",2); + + checkCalc(p,"payload.valueS/payload.valueIB60",2); + checkCalc(p,"payload.valueB/payload.valueIB60",2); + checkCalc(p,"payload.valueF/payload.valueIB60",2f); + checkCalc(p,"payload.valueD/payload.valueIB60",2d); + checkCalc(p,"payload.valueJ/payload.valueIB60",2L); + checkCalc(p,"payload.valueI/payload.valueIB60",2); + + // right is a short + checkCalc(p,"payload.valueSB/payload.valueS",1); + checkCalc(p,"payload.valueBB/payload.valueS",1); + checkCalc(p,"payload.valueFB/payload.valueS",1f); + checkCalc(p,"payload.valueDB/payload.valueS",1d); + checkCalc(p,"payload.valueJB/payload.valueS",1L); + checkCalc(p,"payload.valueIB/payload.valueS",1); + + checkCalc(p,"payload.valueS/payload.valueS",1); + checkCalc(p,"payload.valueB/payload.valueS",1); + checkCalc(p,"payload.valueF/payload.valueS",1f); + checkCalc(p,"payload.valueD/payload.valueS",1d); + checkCalc(p,"payload.valueJ/payload.valueS",1L); + checkCalc(p,"payload.valueI/payload.valueS",1); + + checkCalc(p,"payload.valueSB/payload.valueSB",1); + checkCalc(p,"payload.valueBB/payload.valueSB",1); + checkCalc(p,"payload.valueFB/payload.valueSB",1f); + checkCalc(p,"payload.valueDB/payload.valueSB",1d); + checkCalc(p,"payload.valueJB/payload.valueSB",1L); + checkCalc(p,"payload.valueIB/payload.valueSB",1); + + checkCalc(p,"payload.valueS/payload.valueSB",1); + checkCalc(p,"payload.valueB/payload.valueSB",1); + checkCalc(p,"payload.valueF/payload.valueSB",1f); + checkCalc(p,"payload.valueD/payload.valueSB",1d); + checkCalc(p,"payload.valueJ/payload.valueSB",1L); + checkCalc(p,"payload.valueI/payload.valueSB",1); + + // right is a byte + checkCalc(p,"payload.valueSB/payload.valueB",1); + checkCalc(p,"payload.valueBB/payload.valueB",1); + checkCalc(p,"payload.valueFB/payload.valueB",1f); + checkCalc(p,"payload.valueDB/payload.valueB",1d); + checkCalc(p,"payload.valueJB/payload.valueB",1L); + checkCalc(p,"payload.valueIB/payload.valueB",1); + + checkCalc(p,"payload.valueS/payload.valueB",1); + checkCalc(p,"payload.valueB/payload.valueB",1); + checkCalc(p,"payload.valueF/payload.valueB",1f); + checkCalc(p,"payload.valueD/payload.valueB",1d); + checkCalc(p,"payload.valueJ/payload.valueB",1L); + checkCalc(p,"payload.valueI/payload.valueB",1); + + checkCalc(p,"payload.valueSB/payload.valueBB",1); + checkCalc(p,"payload.valueBB/payload.valueBB",1); + checkCalc(p,"payload.valueFB/payload.valueBB",1f); + checkCalc(p,"payload.valueDB/payload.valueBB",1d); + checkCalc(p,"payload.valueJB/payload.valueBB",1L); + checkCalc(p,"payload.valueIB/payload.valueBB",1); + + checkCalc(p,"payload.valueS/payload.valueBB",1); + checkCalc(p,"payload.valueB/payload.valueBB",1); + checkCalc(p,"payload.valueF/payload.valueBB",1f); + checkCalc(p,"payload.valueD/payload.valueBB",1d); + checkCalc(p,"payload.valueJ/payload.valueBB",1L); + checkCalc(p,"payload.valueI/payload.valueBB",1); + } + + @Test + void opPlus_mixedNumberTypes() { + PayloadX p = new PayloadX(); + + // This is what you had to do before the changes in order for it to compile: + // expression = parse("(T(java.lang.Double).parseDouble(payload.valueI.toString()))/60D"); + + // right is a double + checkCalc(p,"payload.valueSB+60D",180d); + checkCalc(p,"payload.valueBB+60D",180d); + checkCalc(p,"payload.valueFB+60D",180d); + checkCalc(p,"payload.valueDB+60D",180d); + checkCalc(p,"payload.valueJB+60D",180d); + checkCalc(p,"payload.valueIB+60D",180d); + + checkCalc(p,"payload.valueS+60D",180d); + checkCalc(p,"payload.valueB+60D",180d); + checkCalc(p,"payload.valueF+60D",180d); + checkCalc(p,"payload.valueD+60D",180d); + checkCalc(p,"payload.valueJ+60D",180d); + checkCalc(p,"payload.valueI+60D",180d); + + checkCalc(p,"payload.valueSB+payload.valueDB60",180d); + checkCalc(p,"payload.valueBB+payload.valueDB60",180d); + checkCalc(p,"payload.valueFB+payload.valueDB60",180d); + checkCalc(p,"payload.valueDB+payload.valueDB60",180d); + checkCalc(p,"payload.valueJB+payload.valueDB60",180d); + checkCalc(p,"payload.valueIB+payload.valueDB60",180d); + + checkCalc(p,"payload.valueS+payload.valueDB60",180d); + checkCalc(p,"payload.valueB+payload.valueDB60",180d); + checkCalc(p,"payload.valueF+payload.valueDB60",180d); + checkCalc(p,"payload.valueD+payload.valueDB60",180d); + checkCalc(p,"payload.valueJ+payload.valueDB60",180d); + checkCalc(p,"payload.valueI+payload.valueDB60",180d); + + // right is a float + checkCalc(p,"payload.valueSB+60F",180F); + checkCalc(p,"payload.valueBB+60F",180F); + checkCalc(p,"payload.valueFB+60F",180f); + checkCalc(p,"payload.valueDB+60F",180d); + checkCalc(p,"payload.valueJB+60F",180F); + checkCalc(p,"payload.valueIB+60F",180F); + + checkCalc(p,"payload.valueS+60F",180F); + checkCalc(p,"payload.valueB+60F",180F); + checkCalc(p,"payload.valueF+60F",180f); + checkCalc(p,"payload.valueD+60F",180d); + checkCalc(p,"payload.valueJ+60F",180F); + checkCalc(p,"payload.valueI+60F",180F); + + checkCalc(p,"payload.valueSB+payload.valueFB60",180F); + checkCalc(p,"payload.valueBB+payload.valueFB60",180F); + checkCalc(p,"payload.valueFB+payload.valueFB60",180f); + checkCalc(p,"payload.valueDB+payload.valueFB60",180d); + checkCalc(p,"payload.valueJB+payload.valueFB60",180F); + checkCalc(p,"payload.valueIB+payload.valueFB60",180F); + + checkCalc(p,"payload.valueS+payload.valueFB60",180F); + checkCalc(p,"payload.valueB+payload.valueFB60",180F); + checkCalc(p,"payload.valueF+payload.valueFB60",180f); + checkCalc(p,"payload.valueD+payload.valueFB60",180d); + checkCalc(p,"payload.valueJ+payload.valueFB60",180F); + checkCalc(p,"payload.valueI+payload.valueFB60",180F); + + // right is a long + checkCalc(p,"payload.valueSB+60L",180L); + checkCalc(p,"payload.valueBB+60L",180L); + checkCalc(p,"payload.valueFB+60L",180f); + checkCalc(p,"payload.valueDB+60L",180d); + checkCalc(p,"payload.valueJB+60L",180L); + checkCalc(p,"payload.valueIB+60L",180L); + + checkCalc(p,"payload.valueS+60L",180L); + checkCalc(p,"payload.valueB+60L",180L); + checkCalc(p,"payload.valueF+60L",180f); + checkCalc(p,"payload.valueD+60L",180d); + checkCalc(p,"payload.valueJ+60L",180L); + checkCalc(p,"payload.valueI+60L",180L); + + checkCalc(p,"payload.valueSB+payload.valueJB60",180L); + checkCalc(p,"payload.valueBB+payload.valueJB60",180L); + checkCalc(p,"payload.valueFB+payload.valueJB60",180f); + checkCalc(p,"payload.valueDB+payload.valueJB60",180d); + checkCalc(p,"payload.valueJB+payload.valueJB60",180L); + checkCalc(p,"payload.valueIB+payload.valueJB60",180L); + + checkCalc(p,"payload.valueS+payload.valueJB60",180L); + checkCalc(p,"payload.valueB+payload.valueJB60",180L); + checkCalc(p,"payload.valueF+payload.valueJB60",180f); + checkCalc(p,"payload.valueD+payload.valueJB60",180d); + checkCalc(p,"payload.valueJ+payload.valueJB60",180L); + checkCalc(p,"payload.valueI+payload.valueJB60",180L); + + // right is an int + checkCalc(p,"payload.valueSB+60",180); + checkCalc(p,"payload.valueBB+60",180); + checkCalc(p,"payload.valueFB+60",180f); + checkCalc(p,"payload.valueDB+60",180d); + checkCalc(p,"payload.valueJB+60",180L); + checkCalc(p,"payload.valueIB+60",180); + + checkCalc(p,"payload.valueS+60",180); + checkCalc(p,"payload.valueB+60",180); + checkCalc(p,"payload.valueF+60",180f); + checkCalc(p,"payload.valueD+60",180d); + checkCalc(p,"payload.valueJ+60",180L); + checkCalc(p,"payload.valueI+60",180); + + checkCalc(p,"payload.valueSB+payload.valueIB60",180); + checkCalc(p,"payload.valueBB+payload.valueIB60",180); + checkCalc(p,"payload.valueFB+payload.valueIB60",180f); + checkCalc(p,"payload.valueDB+payload.valueIB60",180d); + checkCalc(p,"payload.valueJB+payload.valueIB60",180L); + checkCalc(p,"payload.valueIB+payload.valueIB60",180); + + checkCalc(p,"payload.valueS+payload.valueIB60",180); + checkCalc(p,"payload.valueB+payload.valueIB60",180); + checkCalc(p,"payload.valueF+payload.valueIB60",180f); + checkCalc(p,"payload.valueD+payload.valueIB60",180d); + checkCalc(p,"payload.valueJ+payload.valueIB60",180L); + checkCalc(p,"payload.valueI+payload.valueIB60",180); + + // right is a short + checkCalc(p,"payload.valueSB+payload.valueS",240); + checkCalc(p,"payload.valueBB+payload.valueS",240); + checkCalc(p,"payload.valueFB+payload.valueS",240f); + checkCalc(p,"payload.valueDB+payload.valueS",240d); + checkCalc(p,"payload.valueJB+payload.valueS",240L); + checkCalc(p,"payload.valueIB+payload.valueS",240); + + checkCalc(p,"payload.valueS+payload.valueS",240); + checkCalc(p,"payload.valueB+payload.valueS",240); + checkCalc(p,"payload.valueF+payload.valueS",240f); + checkCalc(p,"payload.valueD+payload.valueS",240d); + checkCalc(p,"payload.valueJ+payload.valueS",240L); + checkCalc(p,"payload.valueI+payload.valueS",240); + + checkCalc(p,"payload.valueSB+payload.valueSB",240); + checkCalc(p,"payload.valueBB+payload.valueSB",240); + checkCalc(p,"payload.valueFB+payload.valueSB",240f); + checkCalc(p,"payload.valueDB+payload.valueSB",240d); + checkCalc(p,"payload.valueJB+payload.valueSB",240L); + checkCalc(p,"payload.valueIB+payload.valueSB",240); + + checkCalc(p,"payload.valueS+payload.valueSB",240); + checkCalc(p,"payload.valueB+payload.valueSB",240); + checkCalc(p,"payload.valueF+payload.valueSB",240f); + checkCalc(p,"payload.valueD+payload.valueSB",240d); + checkCalc(p,"payload.valueJ+payload.valueSB",240L); + checkCalc(p,"payload.valueI+payload.valueSB",240); + + // right is a byte + checkCalc(p,"payload.valueSB+payload.valueB",240); + checkCalc(p,"payload.valueBB+payload.valueB",240); + checkCalc(p,"payload.valueFB+payload.valueB",240f); + checkCalc(p,"payload.valueDB+payload.valueB",240d); + checkCalc(p,"payload.valueJB+payload.valueB",240L); + checkCalc(p,"payload.valueIB+payload.valueB",240); + + checkCalc(p,"payload.valueS+payload.valueB",240); + checkCalc(p,"payload.valueB+payload.valueB",240); + checkCalc(p,"payload.valueF+payload.valueB",240f); + checkCalc(p,"payload.valueD+payload.valueB",240d); + checkCalc(p,"payload.valueJ+payload.valueB",240L); + checkCalc(p,"payload.valueI+payload.valueB",240); + + checkCalc(p,"payload.valueSB+payload.valueBB",240); + checkCalc(p,"payload.valueBB+payload.valueBB",240); + checkCalc(p,"payload.valueFB+payload.valueBB",240f); + checkCalc(p,"payload.valueDB+payload.valueBB",240d); + checkCalc(p,"payload.valueJB+payload.valueBB",240L); + checkCalc(p,"payload.valueIB+payload.valueBB",240); + + checkCalc(p,"payload.valueS+payload.valueBB",240); + checkCalc(p,"payload.valueB+payload.valueBB",240); + checkCalc(p,"payload.valueF+payload.valueBB",240f); + checkCalc(p,"payload.valueD+payload.valueBB",240d); + checkCalc(p,"payload.valueJ+payload.valueBB",240L); + checkCalc(p,"payload.valueI+payload.valueBB",240); + } + + private void checkCalc(PayloadX p, String expression, int expectedResult) { + Expression expr = parse(expression); + assertThat(expr.getValue(p)).isEqualTo(expectedResult); + assertCanCompile(expr); + assertThat(expr.getValue(p)).isEqualTo(expectedResult); + } + + private void checkCalc(PayloadX p, String expression, float expectedResult) { + Expression expr = parse(expression); + assertThat(expr.getValue(p)).isEqualTo(expectedResult); + assertCanCompile(expr); + assertThat(expr.getValue(p)).isEqualTo(expectedResult); + } + + private void checkCalc(PayloadX p, String expression, long expectedResult) { + Expression expr = parse(expression); + assertThat(expr.getValue(p)).isEqualTo(expectedResult); + assertCanCompile(expr); + assertThat(expr.getValue(p)).isEqualTo(expectedResult); + } + + private void checkCalc(PayloadX p, String expression, double expectedResult) { + Expression expr = parse(expression); + assertThat(expr.getValue(p)).isEqualTo(expectedResult); + assertCanCompile(expr); + assertThat(expr.getValue(p)).isEqualTo(expectedResult); + } + + @Test + void opPlusString() { + expression = parse("'hello' + 'world'"); + assertThat(expression.getValue()).isEqualTo("helloworld"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo("helloworld"); + + // Method with string return + expression = parse("'hello' + getWorld()"); + assertThat(expression.getValue(new Greeter())).isEqualTo("helloworld"); + assertCanCompile(expression); + assertThat(expression.getValue(new Greeter())).isEqualTo("helloworld"); + + // Method with string return + expression = parse("getWorld() + 'hello'"); + assertThat(expression.getValue(new Greeter())).isEqualTo("worldhello"); + assertCanCompile(expression); + assertThat(expression.getValue(new Greeter())).isEqualTo("worldhello"); + + // Three strings, optimal bytecode would only use one StringBuilder + expression = parse("'hello' + getWorld() + ' spring'"); + assertThat(expression.getValue(new Greeter())).isEqualTo("helloworld spring"); + assertCanCompile(expression); + assertThat(expression.getValue(new Greeter())).isEqualTo("helloworld spring"); + + // Three strings, optimal bytecode would only use one StringBuilder + expression = parse("'hello' + 3 + ' spring'"); + assertThat(expression.getValue(new Greeter())).isEqualTo("hello3 spring"); + assertCannotCompile(expression); + + expression = parse("object + 'a'"); + assertThat(expression.getValue(new Greeter())).isEqualTo("objecta"); + assertCanCompile(expression); + assertThat(expression.getValue(new Greeter())).isEqualTo("objecta"); + + expression = parse("'a'+object"); + assertThat(expression.getValue(new Greeter())).isEqualTo("aobject"); + assertCanCompile(expression); + assertThat(expression.getValue(new Greeter())).isEqualTo("aobject"); + + expression = parse("'a'+object+'a'"); + assertThat(expression.getValue(new Greeter())).isEqualTo("aobjecta"); + assertCanCompile(expression); + assertThat(expression.getValue(new Greeter())).isEqualTo("aobjecta"); + + expression = parse("object+'a'+object"); + assertThat(expression.getValue(new Greeter())).isEqualTo("objectaobject"); + assertCanCompile(expression); + assertThat(expression.getValue(new Greeter())).isEqualTo("objectaobject"); + + expression = parse("object+object"); + assertThat(expression.getValue(new Greeter())).isEqualTo("objectobject"); + assertCanCompile(expression); + assertThat(expression.getValue(new Greeter())).isEqualTo("objectobject"); + } + + @Test + void opMinus() { + expression = parse("2-2"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(0); + + expression = parse("4L-2L"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(2L); + + expression = parse("4.0f-2.0f"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(2.0f); + + expression = parse("3.0d-4.0d"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(-1.0d); + + expression = parse("-1"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(-1); + + expression = parse("-1L"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(-1L); + + expression = parse("-1.5f"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(-1.5f); + + expression = parse("-2.5d"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(-2.5d); + + expression = parse("T(Integer).valueOf(2)-6"); + assertThat(expression.getValue()).isEqualTo(-4); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(-4); + + expression = parse("T(Integer).valueOf(1)-T(Integer).valueOf(3)"); + assertThat(expression.getValue()).isEqualTo(-2); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(-2); + + expression = parse("4-T(Integer).valueOf(3)"); + assertThat(expression.getValue()).isEqualTo(1); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1); + + expression = parse("T(Float).valueOf(2.0f)-6"); + assertThat(expression.getValue()).isEqualTo(-4.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(-4.0f); + + expression = parse("T(Float).valueOf(8.0f)-T(Float).valueOf(3.0f)"); + assertThat(expression.getValue()).isEqualTo(5.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(5.0f); + + expression = parse("11L-T(Long).valueOf(4L)"); + assertThat(expression.getValue()).isEqualTo(7L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(7L); + + expression = parse("T(Long).valueOf(9L)-6"); + assertThat(expression.getValue()).isEqualTo(3L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(3L); + + expression = parse("T(Long).valueOf(4L)-T(Long).valueOf(3L)"); + assertThat(expression.getValue()).isEqualTo(1L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1L); + + expression = parse("8L-T(Long).valueOf(2L)"); + assertThat(expression.getValue()).isEqualTo(6L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(6L); + } + + @Test + void opMinus_mixedNumberTypes() { + PayloadX p = new PayloadX(); + + // This is what you had to do before the changes in order for it to compile: + // expression = parse("(T(java.lang.Double).parseDouble(payload.valueI.toString()))/60D"); + + // right is a double + checkCalc(p,"payload.valueSB-60D",60d); + checkCalc(p,"payload.valueBB-60D",60d); + checkCalc(p,"payload.valueFB-60D",60d); + checkCalc(p,"payload.valueDB-60D",60d); + checkCalc(p,"payload.valueJB-60D",60d); + checkCalc(p,"payload.valueIB-60D",60d); + + checkCalc(p,"payload.valueS-60D",60d); + checkCalc(p,"payload.valueB-60D",60d); + checkCalc(p,"payload.valueF-60D",60d); + checkCalc(p,"payload.valueD-60D",60d); + checkCalc(p,"payload.valueJ-60D",60d); + checkCalc(p,"payload.valueI-60D",60d); + + checkCalc(p,"payload.valueSB-payload.valueDB60",60d); + checkCalc(p,"payload.valueBB-payload.valueDB60",60d); + checkCalc(p,"payload.valueFB-payload.valueDB60",60d); + checkCalc(p,"payload.valueDB-payload.valueDB60",60d); + checkCalc(p,"payload.valueJB-payload.valueDB60",60d); + checkCalc(p,"payload.valueIB-payload.valueDB60",60d); + + checkCalc(p,"payload.valueS-payload.valueDB60",60d); + checkCalc(p,"payload.valueB-payload.valueDB60",60d); + checkCalc(p,"payload.valueF-payload.valueDB60",60d); + checkCalc(p,"payload.valueD-payload.valueDB60",60d); + checkCalc(p,"payload.valueJ-payload.valueDB60",60d); + checkCalc(p,"payload.valueI-payload.valueDB60",60d); + + // right is a float + checkCalc(p,"payload.valueSB-60F",60F); + checkCalc(p,"payload.valueBB-60F",60F); + checkCalc(p,"payload.valueFB-60F",60f); + checkCalc(p,"payload.valueDB-60F",60d); + checkCalc(p,"payload.valueJB-60F",60F); + checkCalc(p,"payload.valueIB-60F",60F); + + checkCalc(p,"payload.valueS-60F",60F); + checkCalc(p,"payload.valueB-60F",60F); + checkCalc(p,"payload.valueF-60F",60f); + checkCalc(p,"payload.valueD-60F",60d); + checkCalc(p,"payload.valueJ-60F",60F); + checkCalc(p,"payload.valueI-60F",60F); + + checkCalc(p,"payload.valueSB-payload.valueFB60",60F); + checkCalc(p,"payload.valueBB-payload.valueFB60",60F); + checkCalc(p,"payload.valueFB-payload.valueFB60",60f); + checkCalc(p,"payload.valueDB-payload.valueFB60",60d); + checkCalc(p,"payload.valueJB-payload.valueFB60",60F); + checkCalc(p,"payload.valueIB-payload.valueFB60",60F); + + checkCalc(p,"payload.valueS-payload.valueFB60",60F); + checkCalc(p,"payload.valueB-payload.valueFB60",60F); + checkCalc(p,"payload.valueF-payload.valueFB60",60f); + checkCalc(p,"payload.valueD-payload.valueFB60",60d); + checkCalc(p,"payload.valueJ-payload.valueFB60",60F); + checkCalc(p,"payload.valueI-payload.valueFB60",60F); + + // right is a long + checkCalc(p,"payload.valueSB-60L",60L); + checkCalc(p,"payload.valueBB-60L",60L); + checkCalc(p,"payload.valueFB-60L",60f); + checkCalc(p,"payload.valueDB-60L",60d); + checkCalc(p,"payload.valueJB-60L",60L); + checkCalc(p,"payload.valueIB-60L",60L); + + checkCalc(p,"payload.valueS-60L",60L); + checkCalc(p,"payload.valueB-60L",60L); + checkCalc(p,"payload.valueF-60L",60f); + checkCalc(p,"payload.valueD-60L",60d); + checkCalc(p,"payload.valueJ-60L",60L); + checkCalc(p,"payload.valueI-60L",60L); + + checkCalc(p,"payload.valueSB-payload.valueJB60",60L); + checkCalc(p,"payload.valueBB-payload.valueJB60",60L); + checkCalc(p,"payload.valueFB-payload.valueJB60",60f); + checkCalc(p,"payload.valueDB-payload.valueJB60",60d); + checkCalc(p,"payload.valueJB-payload.valueJB60",60L); + checkCalc(p,"payload.valueIB-payload.valueJB60",60L); + + checkCalc(p,"payload.valueS-payload.valueJB60",60L); + checkCalc(p,"payload.valueB-payload.valueJB60",60L); + checkCalc(p,"payload.valueF-payload.valueJB60",60f); + checkCalc(p,"payload.valueD-payload.valueJB60",60d); + checkCalc(p,"payload.valueJ-payload.valueJB60",60L); + checkCalc(p,"payload.valueI-payload.valueJB60",60L); + + // right is an int + checkCalc(p,"payload.valueSB-60",60); + checkCalc(p,"payload.valueBB-60",60); + checkCalc(p,"payload.valueFB-60",60f); + checkCalc(p,"payload.valueDB-60",60d); + checkCalc(p,"payload.valueJB-60",60L); + checkCalc(p,"payload.valueIB-60",60); + + checkCalc(p,"payload.valueS-60",60); + checkCalc(p,"payload.valueB-60",60); + checkCalc(p,"payload.valueF-60",60f); + checkCalc(p,"payload.valueD-60",60d); + checkCalc(p,"payload.valueJ-60",60L); + checkCalc(p,"payload.valueI-60",60); + + checkCalc(p,"payload.valueSB-payload.valueIB60",60); + checkCalc(p,"payload.valueBB-payload.valueIB60",60); + checkCalc(p,"payload.valueFB-payload.valueIB60",60f); + checkCalc(p,"payload.valueDB-payload.valueIB60",60d); + checkCalc(p,"payload.valueJB-payload.valueIB60",60L); + checkCalc(p,"payload.valueIB-payload.valueIB60",60); + + checkCalc(p,"payload.valueS-payload.valueIB60",60); + checkCalc(p,"payload.valueB-payload.valueIB60",60); + checkCalc(p,"payload.valueF-payload.valueIB60",60f); + checkCalc(p,"payload.valueD-payload.valueIB60",60d); + checkCalc(p,"payload.valueJ-payload.valueIB60",60L); + checkCalc(p,"payload.valueI-payload.valueIB60",60); + + // right is a short + checkCalc(p,"payload.valueSB-payload.valueS20",100); + checkCalc(p,"payload.valueBB-payload.valueS20",100); + checkCalc(p,"payload.valueFB-payload.valueS20",100f); + checkCalc(p,"payload.valueDB-payload.valueS20",100d); + checkCalc(p,"payload.valueJB-payload.valueS20",100L); + checkCalc(p,"payload.valueIB-payload.valueS20",100); + + checkCalc(p,"payload.valueS-payload.valueS20",100); + checkCalc(p,"payload.valueB-payload.valueS20",100); + checkCalc(p,"payload.valueF-payload.valueS20",100f); + checkCalc(p,"payload.valueD-payload.valueS20",100d); + checkCalc(p,"payload.valueJ-payload.valueS20",100L); + checkCalc(p,"payload.valueI-payload.valueS20",100); + + checkCalc(p,"payload.valueSB-payload.valueSB20",100); + checkCalc(p,"payload.valueBB-payload.valueSB20",100); + checkCalc(p,"payload.valueFB-payload.valueSB20",100f); + checkCalc(p,"payload.valueDB-payload.valueSB20",100d); + checkCalc(p,"payload.valueJB-payload.valueSB20",100L); + checkCalc(p,"payload.valueIB-payload.valueSB20",100); + + checkCalc(p,"payload.valueS-payload.valueSB20",100); + checkCalc(p,"payload.valueB-payload.valueSB20",100); + checkCalc(p,"payload.valueF-payload.valueSB20",100f); + checkCalc(p,"payload.valueD-payload.valueSB20",100d); + checkCalc(p,"payload.valueJ-payload.valueSB20",100L); + checkCalc(p,"payload.valueI-payload.valueSB20",100); + + // right is a byte + checkCalc(p,"payload.valueSB-payload.valueB20",100); + checkCalc(p,"payload.valueBB-payload.valueB20",100); + checkCalc(p,"payload.valueFB-payload.valueB20",100f); + checkCalc(p,"payload.valueDB-payload.valueB20",100d); + checkCalc(p,"payload.valueJB-payload.valueB20",100L); + checkCalc(p,"payload.valueIB-payload.valueB20",100); + + checkCalc(p,"payload.valueS-payload.valueB20",100); + checkCalc(p,"payload.valueB-payload.valueB20",100); + checkCalc(p,"payload.valueF-payload.valueB20",100f); + checkCalc(p,"payload.valueD-payload.valueB20",100d); + checkCalc(p,"payload.valueJ-payload.valueB20",100L); + checkCalc(p,"payload.valueI-payload.valueB20",100); + + checkCalc(p,"payload.valueSB-payload.valueBB20",100); + checkCalc(p,"payload.valueBB-payload.valueBB20",100); + checkCalc(p,"payload.valueFB-payload.valueBB20",100f); + checkCalc(p,"payload.valueDB-payload.valueBB20",100d); + checkCalc(p,"payload.valueJB-payload.valueBB20",100L); + checkCalc(p,"payload.valueIB-payload.valueBB20",100); + + checkCalc(p,"payload.valueS-payload.valueBB20",100); + checkCalc(p,"payload.valueB-payload.valueBB20",100); + checkCalc(p,"payload.valueF-payload.valueBB20",100f); + checkCalc(p,"payload.valueD-payload.valueBB20",100d); + checkCalc(p,"payload.valueJ-payload.valueBB20",100L); + checkCalc(p,"payload.valueI-payload.valueBB20",100); + } + + @Test + void opMultiply_mixedNumberTypes() { + PayloadX p = new PayloadX(); + + // This is what you had to do before the changes in order for it to compile: + // expression = parse("(T(java.lang.Double).parseDouble(payload.valueI.toString()))/60D"); + + // right is a double + checkCalc(p,"payload.valueSB*60D",7200d); + checkCalc(p,"payload.valueBB*60D",7200d); + checkCalc(p,"payload.valueFB*60D",7200d); + checkCalc(p,"payload.valueDB*60D",7200d); + checkCalc(p,"payload.valueJB*60D",7200d); + checkCalc(p,"payload.valueIB*60D",7200d); + + checkCalc(p,"payload.valueS*60D",7200d); + checkCalc(p,"payload.valueB*60D",7200d); + checkCalc(p,"payload.valueF*60D",7200d); + checkCalc(p,"payload.valueD*60D",7200d); + checkCalc(p,"payload.valueJ*60D",7200d); + checkCalc(p,"payload.valueI*60D",7200d); + + checkCalc(p,"payload.valueSB*payload.valueDB60",7200d); + checkCalc(p,"payload.valueBB*payload.valueDB60",7200d); + checkCalc(p,"payload.valueFB*payload.valueDB60",7200d); + checkCalc(p,"payload.valueDB*payload.valueDB60",7200d); + checkCalc(p,"payload.valueJB*payload.valueDB60",7200d); + checkCalc(p,"payload.valueIB*payload.valueDB60",7200d); + + checkCalc(p,"payload.valueS*payload.valueDB60",7200d); + checkCalc(p,"payload.valueB*payload.valueDB60",7200d); + checkCalc(p,"payload.valueF*payload.valueDB60",7200d); + checkCalc(p,"payload.valueD*payload.valueDB60",7200d); + checkCalc(p,"payload.valueJ*payload.valueDB60",7200d); + checkCalc(p,"payload.valueI*payload.valueDB60",7200d); + + // right is a float + checkCalc(p,"payload.valueSB*60F",7200F); + checkCalc(p,"payload.valueBB*60F",7200F); + checkCalc(p,"payload.valueFB*60F",7200f); + checkCalc(p,"payload.valueDB*60F",7200d); + checkCalc(p,"payload.valueJB*60F",7200F); + checkCalc(p,"payload.valueIB*60F",7200F); + + checkCalc(p,"payload.valueS*60F",7200F); + checkCalc(p,"payload.valueB*60F",7200F); + checkCalc(p,"payload.valueF*60F",7200f); + checkCalc(p,"payload.valueD*60F",7200d); + checkCalc(p,"payload.valueJ*60F",7200F); + checkCalc(p,"payload.valueI*60F",7200F); + + checkCalc(p,"payload.valueSB*payload.valueFB60",7200F); + checkCalc(p,"payload.valueBB*payload.valueFB60",7200F); + checkCalc(p,"payload.valueFB*payload.valueFB60",7200f); + checkCalc(p,"payload.valueDB*payload.valueFB60",7200d); + checkCalc(p,"payload.valueJB*payload.valueFB60",7200F); + checkCalc(p,"payload.valueIB*payload.valueFB60",7200F); + + checkCalc(p,"payload.valueS*payload.valueFB60",7200F); + checkCalc(p,"payload.valueB*payload.valueFB60",7200F); + checkCalc(p,"payload.valueF*payload.valueFB60",7200f); + checkCalc(p,"payload.valueD*payload.valueFB60",7200d); + checkCalc(p,"payload.valueJ*payload.valueFB60",7200F); + checkCalc(p,"payload.valueI*payload.valueFB60",7200F); + + // right is a long + checkCalc(p,"payload.valueSB*60L",7200L); + checkCalc(p,"payload.valueBB*60L",7200L); + checkCalc(p,"payload.valueFB*60L",7200f); + checkCalc(p,"payload.valueDB*60L",7200d); + checkCalc(p,"payload.valueJB*60L",7200L); + checkCalc(p,"payload.valueIB*60L",7200L); + + checkCalc(p,"payload.valueS*60L",7200L); + checkCalc(p,"payload.valueB*60L",7200L); + checkCalc(p,"payload.valueF*60L",7200f); + checkCalc(p,"payload.valueD*60L",7200d); + checkCalc(p,"payload.valueJ*60L",7200L); + checkCalc(p,"payload.valueI*60L",7200L); + + checkCalc(p,"payload.valueSB*payload.valueJB60",7200L); + checkCalc(p,"payload.valueBB*payload.valueJB60",7200L); + checkCalc(p,"payload.valueFB*payload.valueJB60",7200f); + checkCalc(p,"payload.valueDB*payload.valueJB60",7200d); + checkCalc(p,"payload.valueJB*payload.valueJB60",7200L); + checkCalc(p,"payload.valueIB*payload.valueJB60",7200L); + + checkCalc(p,"payload.valueS*payload.valueJB60",7200L); + checkCalc(p,"payload.valueB*payload.valueJB60",7200L); + checkCalc(p,"payload.valueF*payload.valueJB60",7200f); + checkCalc(p,"payload.valueD*payload.valueJB60",7200d); + checkCalc(p,"payload.valueJ*payload.valueJB60",7200L); + checkCalc(p,"payload.valueI*payload.valueJB60",7200L); + + // right is an int + checkCalc(p,"payload.valueSB*60",7200); + checkCalc(p,"payload.valueBB*60",7200); + checkCalc(p,"payload.valueFB*60",7200f); + checkCalc(p,"payload.valueDB*60",7200d); + checkCalc(p,"payload.valueJB*60",7200L); + checkCalc(p,"payload.valueIB*60",7200); + + checkCalc(p,"payload.valueS*60",7200); + checkCalc(p,"payload.valueB*60",7200); + checkCalc(p,"payload.valueF*60",7200f); + checkCalc(p,"payload.valueD*60",7200d); + checkCalc(p,"payload.valueJ*60",7200L); + checkCalc(p,"payload.valueI*60",7200); + + checkCalc(p,"payload.valueSB*payload.valueIB60",7200); + checkCalc(p,"payload.valueBB*payload.valueIB60",7200); + checkCalc(p,"payload.valueFB*payload.valueIB60",7200f); + checkCalc(p,"payload.valueDB*payload.valueIB60",7200d); + checkCalc(p,"payload.valueJB*payload.valueIB60",7200L); + checkCalc(p,"payload.valueIB*payload.valueIB60",7200); + + checkCalc(p,"payload.valueS*payload.valueIB60",7200); + checkCalc(p,"payload.valueB*payload.valueIB60",7200); + checkCalc(p,"payload.valueF*payload.valueIB60",7200f); + checkCalc(p,"payload.valueD*payload.valueIB60",7200d); + checkCalc(p,"payload.valueJ*payload.valueIB60",7200L); + checkCalc(p,"payload.valueI*payload.valueIB60",7200); + + // right is a short + checkCalc(p,"payload.valueSB*payload.valueS20",2400); + checkCalc(p,"payload.valueBB*payload.valueS20",2400); + checkCalc(p,"payload.valueFB*payload.valueS20",2400f); + checkCalc(p,"payload.valueDB*payload.valueS20",2400d); + checkCalc(p,"payload.valueJB*payload.valueS20",2400L); + checkCalc(p,"payload.valueIB*payload.valueS20",2400); + + checkCalc(p,"payload.valueS*payload.valueS20",2400); + checkCalc(p,"payload.valueB*payload.valueS20",2400); + checkCalc(p,"payload.valueF*payload.valueS20",2400f); + checkCalc(p,"payload.valueD*payload.valueS20",2400d); + checkCalc(p,"payload.valueJ*payload.valueS20",2400L); + checkCalc(p,"payload.valueI*payload.valueS20",2400); + + checkCalc(p,"payload.valueSB*payload.valueSB20",2400); + checkCalc(p,"payload.valueBB*payload.valueSB20",2400); + checkCalc(p,"payload.valueFB*payload.valueSB20",2400f); + checkCalc(p,"payload.valueDB*payload.valueSB20",2400d); + checkCalc(p,"payload.valueJB*payload.valueSB20",2400L); + checkCalc(p,"payload.valueIB*payload.valueSB20",2400); + + checkCalc(p,"payload.valueS*payload.valueSB20",2400); + checkCalc(p,"payload.valueB*payload.valueSB20",2400); + checkCalc(p,"payload.valueF*payload.valueSB20",2400f); + checkCalc(p,"payload.valueD*payload.valueSB20",2400d); + checkCalc(p,"payload.valueJ*payload.valueSB20",2400L); + checkCalc(p,"payload.valueI*payload.valueSB20",2400); + + // right is a byte + checkCalc(p,"payload.valueSB*payload.valueB20",2400); + checkCalc(p,"payload.valueBB*payload.valueB20",2400); + checkCalc(p,"payload.valueFB*payload.valueB20",2400f); + checkCalc(p,"payload.valueDB*payload.valueB20",2400d); + checkCalc(p,"payload.valueJB*payload.valueB20",2400L); + checkCalc(p,"payload.valueIB*payload.valueB20",2400); + + checkCalc(p,"payload.valueS*payload.valueB20",2400); + checkCalc(p,"payload.valueB*payload.valueB20",2400); + checkCalc(p,"payload.valueF*payload.valueB20",2400f); + checkCalc(p,"payload.valueD*payload.valueB20",2400d); + checkCalc(p,"payload.valueJ*payload.valueB20",2400L); + checkCalc(p,"payload.valueI*payload.valueB20",2400); + + checkCalc(p,"payload.valueSB*payload.valueBB20",2400); + checkCalc(p,"payload.valueBB*payload.valueBB20",2400); + checkCalc(p,"payload.valueFB*payload.valueBB20",2400f); + checkCalc(p,"payload.valueDB*payload.valueBB20",2400d); + checkCalc(p,"payload.valueJB*payload.valueBB20",2400L); + checkCalc(p,"payload.valueIB*payload.valueBB20",2400); + + checkCalc(p,"payload.valueS*payload.valueBB20",2400); + checkCalc(p,"payload.valueB*payload.valueBB20",2400); + checkCalc(p,"payload.valueF*payload.valueBB20",2400f); + checkCalc(p,"payload.valueD*payload.valueBB20",2400d); + checkCalc(p,"payload.valueJ*payload.valueBB20",2400L); + checkCalc(p,"payload.valueI*payload.valueBB20",2400); + } + + @Test + void opModulus_mixedNumberTypes() { + PayloadX p = new PayloadX(); + + // This is what you had to do before the changes in order for it to compile: + // expression = parse("(T(java.lang.Double).parseDouble(payload.valueI.toString()))/60D"); + + // right is a double + checkCalc(p,"payload.valueSB%58D",4d); + checkCalc(p,"payload.valueBB%58D",4d); + checkCalc(p,"payload.valueFB%58D",4d); + checkCalc(p,"payload.valueDB%58D",4d); + checkCalc(p,"payload.valueJB%58D",4d); + checkCalc(p,"payload.valueIB%58D",4d); + + checkCalc(p,"payload.valueS%58D",4d); + checkCalc(p,"payload.valueB%58D",4d); + checkCalc(p,"payload.valueF%58D",4d); + checkCalc(p,"payload.valueD%58D",4d); + checkCalc(p,"payload.valueJ%58D",4d); + checkCalc(p,"payload.valueI%58D",4d); + + checkCalc(p,"payload.valueSB%payload.valueDB58",4d); + checkCalc(p,"payload.valueBB%payload.valueDB58",4d); + checkCalc(p,"payload.valueFB%payload.valueDB58",4d); + checkCalc(p,"payload.valueDB%payload.valueDB58",4d); + checkCalc(p,"payload.valueJB%payload.valueDB58",4d); + checkCalc(p,"payload.valueIB%payload.valueDB58",4d); + + checkCalc(p,"payload.valueS%payload.valueDB58",4d); + checkCalc(p,"payload.valueB%payload.valueDB58",4d); + checkCalc(p,"payload.valueF%payload.valueDB58",4d); + checkCalc(p,"payload.valueD%payload.valueDB58",4d); + checkCalc(p,"payload.valueJ%payload.valueDB58",4d); + checkCalc(p,"payload.valueI%payload.valueDB58",4d); + + // right is a float + checkCalc(p,"payload.valueSB%58F",4F); + checkCalc(p,"payload.valueBB%58F",4F); + checkCalc(p,"payload.valueFB%58F",4f); + checkCalc(p,"payload.valueDB%58F",4d); + checkCalc(p,"payload.valueJB%58F",4F); + checkCalc(p,"payload.valueIB%58F",4F); + + checkCalc(p,"payload.valueS%58F",4F); + checkCalc(p,"payload.valueB%58F",4F); + checkCalc(p,"payload.valueF%58F",4f); + checkCalc(p,"payload.valueD%58F",4d); + checkCalc(p,"payload.valueJ%58F",4F); + checkCalc(p,"payload.valueI%58F",4F); + + checkCalc(p,"payload.valueSB%payload.valueFB58",4F); + checkCalc(p,"payload.valueBB%payload.valueFB58",4F); + checkCalc(p,"payload.valueFB%payload.valueFB58",4f); + checkCalc(p,"payload.valueDB%payload.valueFB58",4d); + checkCalc(p,"payload.valueJB%payload.valueFB58",4F); + checkCalc(p,"payload.valueIB%payload.valueFB58",4F); + + checkCalc(p,"payload.valueS%payload.valueFB58",4F); + checkCalc(p,"payload.valueB%payload.valueFB58",4F); + checkCalc(p,"payload.valueF%payload.valueFB58",4f); + checkCalc(p,"payload.valueD%payload.valueFB58",4d); + checkCalc(p,"payload.valueJ%payload.valueFB58",4F); + checkCalc(p,"payload.valueI%payload.valueFB58",4F); + + // right is a long + checkCalc(p,"payload.valueSB%58L",4L); + checkCalc(p,"payload.valueBB%58L",4L); + checkCalc(p,"payload.valueFB%58L",4f); + checkCalc(p,"payload.valueDB%58L",4d); + checkCalc(p,"payload.valueJB%58L",4L); + checkCalc(p,"payload.valueIB%58L",4L); + + checkCalc(p,"payload.valueS%58L",4L); + checkCalc(p,"payload.valueB%58L",4L); + checkCalc(p,"payload.valueF%58L",4f); + checkCalc(p,"payload.valueD%58L",4d); + checkCalc(p,"payload.valueJ%58L",4L); + checkCalc(p,"payload.valueI%58L",4L); + + checkCalc(p,"payload.valueSB%payload.valueJB58",4L); + checkCalc(p,"payload.valueBB%payload.valueJB58",4L); + checkCalc(p,"payload.valueFB%payload.valueJB58",4f); + checkCalc(p,"payload.valueDB%payload.valueJB58",4d); + checkCalc(p,"payload.valueJB%payload.valueJB58",4L); + checkCalc(p,"payload.valueIB%payload.valueJB58",4L); + + checkCalc(p,"payload.valueS%payload.valueJB58",4L); + checkCalc(p,"payload.valueB%payload.valueJB58",4L); + checkCalc(p,"payload.valueF%payload.valueJB58",4f); + checkCalc(p,"payload.valueD%payload.valueJB58",4d); + checkCalc(p,"payload.valueJ%payload.valueJB58",4L); + checkCalc(p,"payload.valueI%payload.valueJB58",4L); + + // right is an int + checkCalc(p,"payload.valueSB%58",4); + checkCalc(p,"payload.valueBB%58",4); + checkCalc(p,"payload.valueFB%58",4f); + checkCalc(p,"payload.valueDB%58",4d); + checkCalc(p,"payload.valueJB%58",4L); + checkCalc(p,"payload.valueIB%58",4); + + checkCalc(p,"payload.valueS%58",4); + checkCalc(p,"payload.valueB%58",4); + checkCalc(p,"payload.valueF%58",4f); + checkCalc(p,"payload.valueD%58",4d); + checkCalc(p,"payload.valueJ%58",4L); + checkCalc(p,"payload.valueI%58",4); + + checkCalc(p,"payload.valueSB%payload.valueIB58",4); + checkCalc(p,"payload.valueBB%payload.valueIB58",4); + checkCalc(p,"payload.valueFB%payload.valueIB58",4f); + checkCalc(p,"payload.valueDB%payload.valueIB58",4d); + checkCalc(p,"payload.valueJB%payload.valueIB58",4L); + checkCalc(p,"payload.valueIB%payload.valueIB58",4); + + checkCalc(p,"payload.valueS%payload.valueIB58",4); + checkCalc(p,"payload.valueB%payload.valueIB58",4); + checkCalc(p,"payload.valueF%payload.valueIB58",4f); + checkCalc(p,"payload.valueD%payload.valueIB58",4d); + checkCalc(p,"payload.valueJ%payload.valueIB58",4L); + checkCalc(p,"payload.valueI%payload.valueIB58",4); + + // right is a short + checkCalc(p,"payload.valueSB%payload.valueS18",12); + checkCalc(p,"payload.valueBB%payload.valueS18",12); + checkCalc(p,"payload.valueFB%payload.valueS18",12f); + checkCalc(p,"payload.valueDB%payload.valueS18",12d); + checkCalc(p,"payload.valueJB%payload.valueS18",12L); + checkCalc(p,"payload.valueIB%payload.valueS18",12); + + checkCalc(p,"payload.valueS%payload.valueS18",12); + checkCalc(p,"payload.valueB%payload.valueS18",12); + checkCalc(p,"payload.valueF%payload.valueS18",12f); + checkCalc(p,"payload.valueD%payload.valueS18",12d); + checkCalc(p,"payload.valueJ%payload.valueS18",12L); + checkCalc(p,"payload.valueI%payload.valueS18",12); + + checkCalc(p,"payload.valueSB%payload.valueSB18",12); + checkCalc(p,"payload.valueBB%payload.valueSB18",12); + checkCalc(p,"payload.valueFB%payload.valueSB18",12f); + checkCalc(p,"payload.valueDB%payload.valueSB18",12d); + checkCalc(p,"payload.valueJB%payload.valueSB18",12L); + checkCalc(p,"payload.valueIB%payload.valueSB18",12); + + checkCalc(p,"payload.valueS%payload.valueSB18",12); + checkCalc(p,"payload.valueB%payload.valueSB18",12); + checkCalc(p,"payload.valueF%payload.valueSB18",12f); + checkCalc(p,"payload.valueD%payload.valueSB18",12d); + checkCalc(p,"payload.valueJ%payload.valueSB18",12L); + checkCalc(p,"payload.valueI%payload.valueSB18",12); + + // right is a byte + checkCalc(p,"payload.valueSB%payload.valueB18",12); + checkCalc(p,"payload.valueBB%payload.valueB18",12); + checkCalc(p,"payload.valueFB%payload.valueB18",12f); + checkCalc(p,"payload.valueDB%payload.valueB18",12d); + checkCalc(p,"payload.valueJB%payload.valueB18",12L); + checkCalc(p,"payload.valueIB%payload.valueB18",12); + + checkCalc(p,"payload.valueS%payload.valueB18",12); + checkCalc(p,"payload.valueB%payload.valueB18",12); + checkCalc(p,"payload.valueF%payload.valueB18",12f); + checkCalc(p,"payload.valueD%payload.valueB18",12d); + checkCalc(p,"payload.valueJ%payload.valueB18",12L); + checkCalc(p,"payload.valueI%payload.valueB18",12); + + checkCalc(p,"payload.valueSB%payload.valueBB18",12); + checkCalc(p,"payload.valueBB%payload.valueBB18",12); + checkCalc(p,"payload.valueFB%payload.valueBB18",12f); + checkCalc(p,"payload.valueDB%payload.valueBB18",12d); + checkCalc(p,"payload.valueJB%payload.valueBB18",12L); + checkCalc(p,"payload.valueIB%payload.valueBB18",12); + + checkCalc(p,"payload.valueS%payload.valueBB18",12); + checkCalc(p,"payload.valueB%payload.valueBB18",12); + checkCalc(p,"payload.valueF%payload.valueBB18",12f); + checkCalc(p,"payload.valueD%payload.valueBB18",12d); + checkCalc(p,"payload.valueJ%payload.valueBB18",12L); + checkCalc(p,"payload.valueI%payload.valueBB18",12); + } + + @Test + void opMultiply() { + expression = parse("2*2"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(4); + + expression = parse("2L*2L"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(4L); + + expression = parse("2.0f*2.0f"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(4.0f); + + expression = parse("3.0d*4.0d"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(12.0d); + + expression = parse("T(Float).valueOf(2.0f)*6"); + assertThat(expression.getValue()).isEqualTo(12.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(12.0f); + + expression = parse("T(Float).valueOf(8.0f)*T(Float).valueOf(3.0f)"); + assertThat(expression.getValue()).isEqualTo(24.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(24.0f); + + expression = parse("11L*T(Long).valueOf(4L)"); + assertThat(expression.getValue()).isEqualTo(44L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(44L); + + expression = parse("T(Long).valueOf(9L)*6"); + assertThat(expression.getValue()).isEqualTo(54L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(54L); + + expression = parse("T(Long).valueOf(4L)*T(Long).valueOf(3L)"); + assertThat(expression.getValue()).isEqualTo(12L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(12L); + + expression = parse("8L*T(Long).valueOf(2L)"); + assertThat(expression.getValue()).isEqualTo(16L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(16L); + + expression = parse("T(Float).valueOf(8.0f)*-T(Float).valueOf(3.0f)"); + assertThat(expression.getValue()).isEqualTo(-24.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(-24.0f); + } + + @Test + void opDivide() { + expression = parse("2/2"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1); + + expression = parse("2L/2L"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1L); + + expression = parse("2.0f/2.0f"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1.0f); + + expression = parse("3.0d/4.0d"); + expression.getValue(); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(0.75d); + + expression = parse("T(Float).valueOf(6.0f)/2"); + assertThat(expression.getValue()).isEqualTo(3.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(3.0f); + + expression = parse("T(Float).valueOf(8.0f)/T(Float).valueOf(2.0f)"); + assertThat(expression.getValue()).isEqualTo(4.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(4.0f); + + expression = parse("12L/T(Long).valueOf(4L)"); + assertThat(expression.getValue()).isEqualTo(3L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(3L); + + expression = parse("T(Long).valueOf(44L)/11"); + assertThat(expression.getValue()).isEqualTo(4L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(4L); + + expression = parse("T(Long).valueOf(4L)/T(Long).valueOf(2L)"); + assertThat(expression.getValue()).isEqualTo(2L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(2L); + + expression = parse("8L/T(Long).valueOf(2L)"); + assertThat(expression.getValue()).isEqualTo(4L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(4L); + + expression = parse("T(Float).valueOf(8.0f)/-T(Float).valueOf(4.0f)"); + assertThat(expression.getValue()).isEqualTo(-2.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(-2.0f); + } + + @Test + void opModulus_12041() { + expression = parse("2%2"); + assertThat(expression.getValue()).isEqualTo(0); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(0); + + expression = parse("payload%2==0"); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4), boolean.class)).isTrue(); + assertThat(expression.getValue(new GenericMessageTestHelper<>(5), boolean.class)).isFalse(); + assertCanCompile(expression); + assertThat(expression.getValue(new GenericMessageTestHelper<>(4), boolean.class)).isTrue(); + assertThat(expression.getValue(new GenericMessageTestHelper<>(5), boolean.class)).isFalse(); + + expression = parse("8%3"); + assertThat(expression.getValue()).isEqualTo(2); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(2); + + expression = parse("17L%5L"); + assertThat(expression.getValue()).isEqualTo(2L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(2L); + + expression = parse("3.0f%2.0f"); + assertThat(expression.getValue()).isEqualTo(1.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1.0f); + + expression = parse("3.0d%4.0d"); + assertThat(expression.getValue()).isEqualTo(3.0d); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(3.0d); + + expression = parse("T(Float).valueOf(6.0f)%2"); + assertThat(expression.getValue()).isEqualTo(0.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(0.0f); + + expression = parse("T(Float).valueOf(6.0f)%4"); + assertThat(expression.getValue()).isEqualTo(2.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(2.0f); + + expression = parse("T(Float).valueOf(8.0f)%T(Float).valueOf(3.0f)"); + assertThat(expression.getValue()).isEqualTo(2.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(2.0f); + + expression = parse("13L%T(Long).valueOf(4L)"); + assertThat(expression.getValue()).isEqualTo(1L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1L); + + expression = parse("T(Long).valueOf(44L)%12"); + assertThat(expression.getValue()).isEqualTo(8L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(8L); + + expression = parse("T(Long).valueOf(9L)%T(Long).valueOf(2L)"); + assertThat(expression.getValue()).isEqualTo(1L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1L); + + expression = parse("7L%T(Long).valueOf(2L)"); + assertThat(expression.getValue()).isEqualTo(1L); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1L); + + expression = parse("T(Float).valueOf(9.0f)%-T(Float).valueOf(4.0f)"); + assertThat(expression.getValue()).isEqualTo(1.0f); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(1.0f); + } + } + + @Nested + class TypeReferenceTests { + + @Test + void typeReference() { + expression = parse("T(String)"); + assertThat(expression.getValue()).isEqualTo(String.class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(String.class); + + expression = parse("T(java.io.IOException)"); + assertThat(expression.getValue()).isEqualTo(IOException.class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(IOException.class); + + expression = parse("T(java.io.IOException[])"); + assertThat(expression.getValue()).isEqualTo(IOException[].class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(IOException[].class); + + expression = parse("T(int[][])"); + assertThat(expression.getValue()).isEqualTo(int[][].class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(int[][].class); + + expression = parse("T(int)"); + assertThat(expression.getValue()).isEqualTo(int.class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(int.class); + + expression = parse("T(byte)"); + assertThat(expression.getValue()).isEqualTo(byte.class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(byte.class); + + expression = parse("T(char)"); + assertThat(expression.getValue()).isEqualTo(char.class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(char.class); + + expression = parse("T(short)"); + assertThat(expression.getValue()).isEqualTo(short.class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(short.class); + + expression = parse("T(long)"); + assertThat(expression.getValue()).isEqualTo(long.class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(long.class); + + expression = parse("T(float)"); + assertThat(expression.getValue()).isEqualTo(float.class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(float.class); + + expression = parse("T(double)"); + assertThat(expression.getValue()).isEqualTo(double.class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(double.class); + + expression = parse("T(boolean)"); + assertThat(expression.getValue()).isEqualTo(boolean.class); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo(boolean.class); + + expression = parse("T(Missing)"); + assertThatExceptionOfType(SpelEvaluationException.class) + .isThrownBy(expression::getValue) + .withMessageEndingWith("Type cannot be found 'Missing'"); + assertCannotCompile(expression); + } + } + + @Nested + class NullSafeNavigationTests { + + @Test + void nullsafeFieldPropertyDereferencing_SPR16489() { + FooObjectHolder foh = new FooObjectHolder(); + StandardEvaluationContext context = new StandardEvaluationContext(); + context.setRootObject(foh); + + // First non compiled: + SpelExpression expression = (SpelExpression) parser.parseExpression("foo?.object"); + assertThat(expression.getValue(context)).isEqualTo("hello"); + foh.foo = null; + assertThat(expression.getValue(context)).isNull(); + + // Now revert state of foh and try compiling it: + foh.foo = new FooObject(); + assertThat(expression.getValue(context)).isEqualTo("hello"); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo("hello"); + foh.foo = null; + assertThat(expression.getValue(context)).isNull(); + + // Static references + expression = (SpelExpression) parser.parseExpression("#var?.propertya"); + context.setVariable("var", StaticsHelper.class); + assertThat(expression.getValue(context).toString()).isEqualTo("sh"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + assertCanCompile(expression); + context.setVariable("var", StaticsHelper.class); + assertThat(expression.getValue(context).toString()).isEqualTo("sh"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + + // Single size primitive (boolean) + expression = (SpelExpression) parser.parseExpression("#var?.a"); + context.setVariable("var", new TestClass4()); + assertThat((Boolean) expression.getValue(context)).isFalse(); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + assertCanCompile(expression); + context.setVariable("var", new TestClass4()); + assertThat((Boolean) expression.getValue(context)).isFalse(); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + + // Double slot primitives + expression = (SpelExpression) parser.parseExpression("#var?.four"); + context.setVariable("var", new Three()); + assertThat(expression.getValue(context).toString()).isEqualTo("0.04"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + assertCanCompile(expression); + context.setVariable("var", new Three()); + assertThat(expression.getValue(context).toString()).isEqualTo("0.04"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + } + + @Test // gh-27421 + void nullSafeMethodChainingWithNonStaticVoidMethod() { + FooObjectHolder foh = new FooObjectHolder(); + StandardEvaluationContext context = new StandardEvaluationContext(foh); + SpelExpression expression = (SpelExpression) parser.parseExpression("getFoo()?.doFoo()"); + + FooObject.doFooInvoked = false; + assertThat(expression.getValue(context)).isNull(); + assertThat(FooObject.doFooInvoked).isTrue(); + + FooObject.doFooInvoked = false; + foh.foo = null; + assertThat(expression.getValue(context)).isNull(); + assertThat(FooObject.doFooInvoked).isFalse(); + + assertCanCompile(expression); + + FooObject.doFooInvoked = false; + foh.foo = new FooObject(); + assertThat(expression.getValue(context)).isNull(); + assertThat(FooObject.doFooInvoked).isTrue(); + + FooObject.doFooInvoked = false; + foh.foo = null; + assertThat(expression.getValue(context)).isNull(); + assertThat(FooObject.doFooInvoked).isFalse(); + } + + @Test + void nullsafeMethodChaining_SPR16489() { + FooObjectHolder foh = new FooObjectHolder(); + StandardEvaluationContext context = new StandardEvaluationContext(); + context.setRootObject(foh); + + // First non compiled: + SpelExpression expression = (SpelExpression) parser.parseExpression("getFoo()?.getObject()"); + assertThat(expression.getValue(context)).isEqualTo("hello"); + foh.foo = null; + assertThat(expression.getValue(context)).isNull(); + assertCanCompile(expression); + foh.foo = new FooObject(); + assertThat(expression.getValue(context)).isEqualTo("hello"); + foh.foo = null; + assertThat(expression.getValue(context)).isNull(); + + // Static method references + expression = (SpelExpression) parser.parseExpression("#var?.methoda()"); + context.setVariable("var", StaticsHelper.class); + assertThat(expression.getValue(context).toString()).isEqualTo("sh"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + assertCanCompile(expression); + context.setVariable("var", StaticsHelper.class); + assertThat(expression.getValue(context).toString()).isEqualTo("sh"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + + // Nullsafe guard on expression element evaluating to primitive/null + expression = (SpelExpression) parser.parseExpression("#var?.intValue()"); + context.setVariable("var", 4); + assertThat(expression.getValue(context).toString()).isEqualTo("4"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + assertCanCompile(expression); + context.setVariable("var", 4); + assertThat(expression.getValue(context).toString()).isEqualTo("4"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + + // Nullsafe guard on expression element evaluating to primitive/null + expression = (SpelExpression) parser.parseExpression("#var?.booleanValue()"); + context.setVariable("var", false); + assertThat(expression.getValue(context).toString()).isEqualTo("false"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + assertCanCompile(expression); + context.setVariable("var", false); + assertThat(expression.getValue(context).toString()).isEqualTo("false"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + + // Nullsafe guard on expression element evaluating to primitive/null + expression = (SpelExpression) parser.parseExpression("#var?.booleanValue()"); + context.setVariable("var", true); + assertThat(expression.getValue(context).toString()).isEqualTo("true"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + assertCanCompile(expression); + context.setVariable("var", true); + assertThat(expression.getValue(context).toString()).isEqualTo("true"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + + // Nullsafe guard on expression element evaluating to primitive/null + expression = (SpelExpression) parser.parseExpression("#var?.longValue()"); + context.setVariable("var", 5L); + assertThat(expression.getValue(context).toString()).isEqualTo("5"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + assertCanCompile(expression); + context.setVariable("var", 5L); + assertThat(expression.getValue(context).toString()).isEqualTo("5"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + + // Nullsafe guard on expression element evaluating to primitive/null + expression = (SpelExpression) parser.parseExpression("#var?.floatValue()"); + context.setVariable("var", 3f); + assertThat(expression.getValue(context).toString()).isEqualTo("3.0"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + assertCanCompile(expression); + context.setVariable("var", 3f); + assertThat(expression.getValue(context).toString()).isEqualTo("3.0"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + + // Nullsafe guard on expression element evaluating to primitive/null + expression = (SpelExpression) parser.parseExpression("#var?.shortValue()"); + context.setVariable("var", (short)8); + assertThat(expression.getValue(context).toString()).isEqualTo("8"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + assertCanCompile(expression); + context.setVariable("var", (short)8); + assertThat(expression.getValue(context).toString()).isEqualTo("8"); + context.setVariable("var", null); + assertThat(expression.getValue(context)).isNull(); + } + + @Test + void compilationOfBasicNullSafeMethodReference() { + SpelExpressionParser parser = new SpelExpressionParser( + new SpelParserConfiguration(SpelCompilerMode.OFF, getClass().getClassLoader())); + SpelExpression expression = parser.parseRaw("#it?.equals(3)"); + StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {1}); + context.setVariable("it", 3); + expression.setEvaluationContext(context); + assertThat(expression.getValue(Boolean.class)).isTrue(); + context.setVariable("it", null); + assertThat(expression.getValue(Boolean.class)).isNull(); + + assertCanCompile(expression); + + context.setVariable("it", 3); + assertThat(expression.getValue(Boolean.class)).isTrue(); + context.setVariable("it", null); + assertThat(expression.getValue(Boolean.class)).isNull(); + } + + @Test // gh-27421 + void nullSafeInvocationOfNonStaticVoidMethod() { + // non-static method, no args, void return + expression = parser.parseExpression("new %s()?.one()".formatted(TestClass5.class.getName())); + + assertCannotCompile(expression); + + TestClass5._i = 0; + assertThat(expression.getValue()).isNull(); + assertThat(TestClass5._i).isEqualTo(1); + + TestClass5._i = 0; + assertCanCompile(expression); + assertThat(expression.getValue()).isNull(); + assertThat(TestClass5._i).isEqualTo(1); + } + + @Test // gh-27421 + void nullSafeInvocationOfStaticVoidMethod() { + // static method, no args, void return + expression = parser.parseExpression("T(%s)?.two()".formatted(TestClass5.class.getName())); + + assertCannotCompile(expression); + + TestClass5._i = 0; + assertThat(expression.getValue()).isNull(); + assertThat(TestClass5._i).isEqualTo(1); + + TestClass5._i = 0; + assertCanCompile(expression); + assertThat(expression.getValue()).isNull(); + assertThat(TestClass5._i).isEqualTo(1); + } + + @Test // gh-27421 + void nullSafeInvocationOfNonStaticVoidWrapperMethod() { + // non-static method, no args, Void return + expression = parser.parseExpression("new %s()?.oneVoidWrapper()".formatted(TestClass5.class.getName())); + + assertCannotCompile(expression); + + TestClass5._i = 0; + assertThat(expression.getValue()).isNull(); + assertThat(TestClass5._i).isEqualTo(1); + + TestClass5._i = 0; + assertCanCompile(expression); + assertThat(expression.getValue()).isNull(); + assertThat(TestClass5._i).isEqualTo(1); + } + + @Test // gh-27421 + void nullSafeInvocationOfStaticVoidWrapperMethod() { + // static method, no args, Void return + expression = parser.parseExpression("T(%s)?.twoVoidWrapper()".formatted(TestClass5.class.getName())); + + assertCannotCompile(expression); + + TestClass5._i = 0; + assertThat(expression.getValue()).isNull(); + assertThat(TestClass5._i).isEqualTo(1); + + TestClass5._i = 0; + assertCanCompile(expression); + assertThat(expression.getValue()).isNull(); + assertThat(TestClass5._i).isEqualTo(1); + } + } + + @Nested + class VariableAndFunctionAccessTests { @ParameterizedTest // gh-32356 @ValueSource(strings = { "#root", "#this" }) @@ -235,6 +3574,1324 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { .withCauseInstanceOf(ClassCastException.class); } + @Test + void functionReference() throws Exception { + EvaluationContext ctx = new StandardEvaluationContext(); + Method m = SpelCompilationCoverageTests.class.getDeclaredMethod("concat", String.class, String.class); + ctx.setVariable("concat", m); + Method m2 = SpelCompilationCoverageTests.class.getDeclaredMethod("concat2", Object[].class); + ctx.setVariable("concat2", m2); + Method m3 = SpelCompilationCoverageTests.class.getDeclaredMethod("join", String[].class); + ctx.setVariable("join", m3); + + expression = parser.parseExpression("#concat('a','b')"); + assertThat(expression.getValue(ctx)).isEqualTo("ab"); + assertCanCompile(expression); + assertThat(expression.getValue(ctx)).isEqualTo("ab"); + + expression = parser.parseExpression("#concat(#concat('a','b'),'c').charAt(1)"); + assertThat(expression.getValue(ctx)).isEqualTo('b'); + assertCanCompile(expression); + assertThat(expression.getValue(ctx)).isEqualTo('b'); + + // varargs + expression = parser.parseExpression("#join(#stringArray)"); + ctx.setVariable("stringArray", new String[] { "a", "b", "c" }); + assertThat(expression.getValue(ctx)).isEqualTo("abc"); + assertCanCompile(expression); + assertThat(expression.getValue(ctx)).isEqualTo("abc"); + + // varargs with argument component type that is a subtype of the varargs component type. + expression = parser.parseExpression("#concat2(#stringArray)"); + ctx.setVariable("stringArray", new String[] { "a", "b", "c" }); + assertThat(expression.getValue(ctx)).isEqualTo("abc"); + assertCanCompile(expression); + assertThat(expression.getValue(ctx)).isEqualTo("abc"); + + expression = parser.parseExpression("#concat(#a,#b)"); + ctx.setVariable("a", "foo"); + ctx.setVariable("b", "bar"); + assertThat(expression.getValue(ctx)).isEqualTo("foobar"); + assertCanCompile(expression); + assertThat(expression.getValue(ctx)).isEqualTo("foobar"); + ctx.setVariable("b", "boo"); + assertThat(expression.getValue(ctx)).isEqualTo("fooboo"); + + m = Math.class.getDeclaredMethod("pow", double.class, double.class); + ctx.setVariable("kapow",m); + expression = parser.parseExpression("#kapow(2.0d,2.0d)"); + assertThat(expression.getValue(ctx).toString()).isEqualTo("4.0"); + assertCanCompile(expression); + assertThat(expression.getValue(ctx).toString()).isEqualTo("4.0"); + } + + @ParameterizedTest + @ValueSource(strings = {"voidMethod", "voidWrapperMethod"}) + void voidFunctionReference(String method) throws Exception { + assertVoidFunctionReferenceBehavior(method); + } + + private void assertVoidFunctionReferenceBehavior(String methodName) throws Exception { + Method method = SpelCompilationCoverageTests.class.getDeclaredMethod(methodName, String.class); + + EvaluationContext ctx = new StandardEvaluationContext(); + ctx.setVariable("voidMethod", method); + + expression = parser.parseExpression("#voidMethod('a')"); + + voidMethodInvokedWith = null; + expression.getValue(ctx); + assertThat(voidMethodInvokedWith).isEqualTo("a"); + assertCanCompile(expression); + + voidMethodInvokedWith = null; + expression.getValue(ctx); + assertThat(voidMethodInvokedWith).isEqualTo("a"); + assertCanCompile(expression); + + voidMethodInvokedWith = null; + expression.getValue(ctx); + assertThat(voidMethodInvokedWith).isEqualTo("a"); + assertCanCompile(expression); + + expression = parser.parseExpression("#voidMethod(#a)"); + ctx.setVariable("a", "foo"); + + voidMethodInvokedWith = null; + expression.getValue(ctx); + assertThat(voidMethodInvokedWith).isEqualTo("foo"); + assertCanCompile(expression); + + voidMethodInvokedWith = null; + expression.getValue(ctx); + assertThat(voidMethodInvokedWith).isEqualTo("foo"); + assertCanCompile(expression); + } + + @Test + void functionReferenceVisibility_SPR12359() throws Exception { + // Confirms visibility of what is being called. + StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {"1"}); + context.registerFunction("doCompare", SomeCompareMethod.class.getDeclaredMethod( + "compare", Object.class, Object.class)); + context.setVariable("arg", "2"); + // type nor method are public + expression = parser.parseExpression("#doCompare([0],#arg)"); + assertThat(expression.getValue(context, Integer.class).toString()).isEqualTo("-1"); + assertCannotCompile(expression); + + // type not public but method is + context = new StandardEvaluationContext(new Object[] {"1"}); + context.registerFunction("doCompare", SomeCompareMethod.class.getDeclaredMethod( + "compare2", Object.class, Object.class)); + context.setVariable("arg", "2"); + expression = parser.parseExpression("#doCompare([0],#arg)"); + assertThat(expression.getValue(context, Integer.class).toString()).isEqualTo("-1"); + assertCannotCompile(expression); + } + + @Test + void functionReferenceNonCompilableArguments_SPR12359() throws Exception { + StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {"1"}); + context.registerFunction("negate", SomeCompareMethod2.class.getDeclaredMethod( + "negate", int.class)); + context.setVariable("arg", "2"); + int[] ints = new int[] {1,2,3}; + context.setVariable("ints",ints); + + expression = parser.parseExpression("#negate(#ints.?[#this<2][0])"); + assertThat(expression.getValue(context, Integer.class).toString()).isEqualTo("-1"); + // Selection isn't compilable. + assertThat(((SpelExpression) expression).getAST().isCompilable()).isFalse(); + } + + @Test + void functionReferenceVarargs_SPR12359() throws Exception { + StandardEvaluationContext context = new StandardEvaluationContext(); + context.registerFunction("append", + SomeCompareMethod2.class.getDeclaredMethod("append", String[].class)); + context.registerFunction("append2", + SomeCompareMethod2.class.getDeclaredMethod("append2", Object[].class)); + context.registerFunction("append3", + SomeCompareMethod2.class.getDeclaredMethod("append3", String[].class)); + context.registerFunction("append4", + SomeCompareMethod2.class.getDeclaredMethod("append4", String.class, String[].class)); + context.registerFunction("appendChar", + SomeCompareMethod2.class.getDeclaredMethod("appendChar", char[].class)); + context.registerFunction("sum", + SomeCompareMethod2.class.getDeclaredMethod("sum", int[].class)); + context.registerFunction("sumDouble", + SomeCompareMethod2.class.getDeclaredMethod("sumDouble", double[].class)); + context.registerFunction("sumFloat", + SomeCompareMethod2.class.getDeclaredMethod("sumFloat", float[].class)); + context.setVariable("stringArray", new String[] {"x","y","z"}); + context.setVariable("intArray", new int[] {5,6,9}); + context.setVariable("doubleArray", new double[] {5.0d,6.0d,9.0d}); + context.setVariable("floatArray", new float[] {5.0f,6.0f,9.0f}); + + expression = parser.parseExpression("#append('a','b','c')"); + assertThat(expression.getValue(context).toString()).isEqualTo("abc"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context).toString()).isEqualTo("abc"); + + expression = parser.parseExpression("#append('a')"); + assertThat(expression.getValue(context).toString()).isEqualTo("a"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context).toString()).isEqualTo("a"); + + expression = parser.parseExpression("#append()"); + assertThat(expression.getValue(context).toString()).isEmpty(); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context).toString()).isEmpty(); + + expression = parser.parseExpression("#append(#stringArray)"); + assertThat(expression.getValue(context).toString()).isEqualTo("xyz"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context).toString()).isEqualTo("xyz"); + + // This is a methodreference invocation, to compare with functionreference + expression = parser.parseExpression("append(#stringArray)"); + assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("xyz"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("xyz"); + + expression = parser.parseExpression("#append2('a','b','c')"); + assertThat(expression.getValue(context).toString()).isEqualTo("abc"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context).toString()).isEqualTo("abc"); + + expression = parser.parseExpression("append2('a','b')"); + assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("ab"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("ab"); + + expression = parser.parseExpression("#append2('a','b')"); + assertThat(expression.getValue(context).toString()).isEqualTo("ab"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context).toString()).isEqualTo("ab"); + + expression = parser.parseExpression("#append2()"); + assertThat(expression.getValue(context).toString()).isEmpty(); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context).toString()).isEmpty(); + + expression = parser.parseExpression("#append3(#stringArray)"); + assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("xyz"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("xyz"); + + expression = parser.parseExpression("#append2(#stringArray)"); + assertThat(expression.getValue(context)).hasToString("xyz"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).hasToString("xyz"); + + expression = parser.parseExpression("#sum(1,2,3)"); + assertThat(expression.getValue(context)).isEqualTo(6); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(6); + + expression = parser.parseExpression("#sum(2)"); + assertThat(expression.getValue(context)).isEqualTo(2); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(2); + + expression = parser.parseExpression("#sum()"); + assertThat(expression.getValue(context)).isEqualTo(0); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(0); + + expression = parser.parseExpression("#sum(#intArray)"); + assertThat(expression.getValue(context)).isEqualTo(20); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(20); + + expression = parser.parseExpression("#sumDouble(1.0d,2.0d,3.0d)"); + assertThat(expression.getValue(context)).isEqualTo(6); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(6); + + expression = parser.parseExpression("#sumDouble(2.0d)"); + assertThat(expression.getValue(context)).isEqualTo(2); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(2); + + expression = parser.parseExpression("#sumDouble()"); + assertThat(expression.getValue(context)).isEqualTo(0); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(0); + + expression = parser.parseExpression("#sumDouble(#doubleArray)"); + assertThat(expression.getValue(context)).isEqualTo(20); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(20); + + expression = parser.parseExpression("#sumFloat(1.0f,2.0f,3.0f)"); + assertThat(expression.getValue(context)).isEqualTo(6); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(6); + + expression = parser.parseExpression("#sumFloat(2.0f)"); + assertThat(expression.getValue(context)).isEqualTo(2); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(2); + + expression = parser.parseExpression("#sumFloat()"); + assertThat(expression.getValue(context)).isEqualTo(0); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(0); + + expression = parser.parseExpression("#sumFloat(#floatArray)"); + assertThat(expression.getValue(context)).isEqualTo(20); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo(20); + + expression = parser.parseExpression("#appendChar('abc'.charAt(0),'abc'.charAt(1))"); + assertThat(expression.getValue(context)).isEqualTo("ab"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo("ab"); + + expression = parser.parseExpression("#append4('a','b','c')"); + assertThat(expression.getValue(context).toString()).isEqualTo("a::bc"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context).toString()).isEqualTo("a::bc"); + + expression = parser.parseExpression("#append4('a','b')"); + assertThat(expression.getValue(context).toString()).isEqualTo("a::b"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context).toString()).isEqualTo("a::b"); + + expression = parser.parseExpression("#append4('a')"); + assertThat(expression.getValue(context).toString()).isEqualTo("a::"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context).toString()).isEqualTo("a::"); + + expression = parser.parseExpression("#append4('a',#stringArray)"); + assertThat(expression.getValue(context).toString()).isEqualTo("a::xyz"); + assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context).toString()).isEqualTo("a::xyz"); + } + + @Test + void functionReferenceVarargs() throws Exception { + EvaluationContext ctx = new StandardEvaluationContext(); + Method m = SpelCompilationCoverageTests.class.getDeclaredMethod("join", String[].class); + ctx.setVariable("join", m); + expression = parser.parseExpression("#join('a','b','c')"); + assertThat(expression.getValue(ctx)).isEqualTo("abc"); + assertCanCompile(expression); + assertThat(expression.getValue(ctx)).isEqualTo("abc"); + } + } + + @Nested + class MethodAndConstructorTests { + + @Test + void constructorReference_SPR13781() { + // Static field access on a T() referenced type + expression = parser.parseExpression("T(java.util.Locale).ENGLISH"); + assertThat(expression.getValue().toString()).isEqualTo("en"); + assertCanCompile(expression); + assertThat(expression.getValue().toString()).isEqualTo("en"); + + // The actual expression from the bug report. It fails if the ENGLISH reference fails + // to pop the type reference for Locale off the stack (if it isn't popped then + // toLowerCase() will be called with a Locale parameter). In this situation the + // code generation for ENGLISH should notice there is something on the stack that + // is not required and pop it off. + expression = parser.parseExpression("#userId.toString().toLowerCase(T(java.util.Locale).ENGLISH)"); + StandardEvaluationContext context = new StandardEvaluationContext(); + context.setVariable("userId", "RoDnEy"); + assertThat(expression.getValue(context)).isEqualTo("rodney"); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo("rodney"); + + // Property access on a class object + expression = parser.parseExpression("T(String).name"); + assertThat(expression.getValue()).isEqualTo("java.lang.String"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo("java.lang.String"); + + // Now the type reference isn't on the stack, and needs loading + context = new StandardEvaluationContext(String.class); + expression = parser.parseExpression("name"); + assertThat(expression.getValue(context)).isEqualTo("java.lang.String"); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo("java.lang.String"); + + expression = parser.parseExpression("T(String).getName()"); + assertThat(expression.getValue()).isEqualTo("java.lang.String"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo("java.lang.String"); + + // These tests below verify that the chain of static accesses (either method/property or field) + // leave the right thing on top of the stack for processing by any outer consuming code. + // Here the consuming code is the String.valueOf() function. If the wrong thing were on + // the stack (for example if the compiled code for static methods wasn't popping the + // previous thing off the stack) the valueOf() would operate on the wrong value. + + String shclass = StaticsHelper.class.getName(); + // Basic chain: property access then method access + expression = parser.parseExpression("T(String).valueOf(T(String).name.valueOf(1))"); + assertThat(expression.getValue()).isEqualTo("1"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo("1"); + + // chain of statics ending with static method + expression = parser.parseExpression("T(String).valueOf(T(" + shclass + ").methoda().methoda().methodb())"); + assertThat(expression.getValue()).isEqualTo("mb"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo("mb"); + + // chain of statics ending with static field + expression = parser.parseExpression("T(String).valueOf(T(" + shclass + ").fielda.fielda.fieldb)"); + assertThat(expression.getValue()).isEqualTo("fb"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo("fb"); + + // chain of statics ending with static property access + expression = parser.parseExpression("T(String).valueOf(T(" + shclass + ").propertya.propertya.propertyb)"); + assertThat(expression.getValue()).isEqualTo("pb"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo("pb"); + + // variety chain + expression = parser.parseExpression("T(String).valueOf(T(" + shclass + ").fielda.methoda().propertya.fieldb)"); + assertThat(expression.getValue()).isEqualTo("fb"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo("fb"); + + expression = parser.parseExpression("T(String).valueOf(fielda.fieldb)"); + assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("fb"); + assertCanCompile(expression); + assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("fb"); + + expression = parser.parseExpression("T(String).valueOf(propertya.propertyb)"); + assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("pb"); + assertCanCompile(expression); + assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("pb"); + + expression = parser.parseExpression("T(String).valueOf(methoda().methodb())"); + assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("mb"); + assertCanCompile(expression); + assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("mb"); + } + + @Test + void constructorReference_SPR12326() { + String type = SpelCompilationCoverageTests.class.getName(); + String prefix = "new " + type + ".Obj"; + + expression = parser.parseExpression(prefix + "([0])"); + assertThat(((Obj) expression.getValue(new Object[]{"test"})).param1).isEqualTo("test"); + assertCanCompile(expression); + assertThat(((Obj) expression.getValue(new Object[]{"test"})).param1).isEqualTo("test"); + + expression = parser.parseExpression(prefix + "2('foo','bar').output"); + assertThat(expression.getValue(String.class)).isEqualTo("foobar"); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("foobar"); + + expression = parser.parseExpression(prefix + "2('foo').output"); + assertThat(expression.getValue(String.class)).isEqualTo("foo"); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("foo"); + + expression = parser.parseExpression(prefix + "2().output"); + assertThat(expression.getValue(String.class)).isEmpty(); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEmpty(); + + expression = parser.parseExpression(prefix + "3(1,2,3).output"); + assertThat(expression.getValue(String.class)).isEqualTo("123"); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("123"); + + expression = parser.parseExpression(prefix + "3(1).output"); + assertThat(expression.getValue(String.class)).isEqualTo("1"); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("1"); + + expression = parser.parseExpression(prefix + "3().output"); + assertThat(expression.getValue(String.class)).isEmpty(); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEmpty(); + + expression = parser.parseExpression(prefix + "3('abc',5.0f,1,2,3).output"); + assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:123"); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:123"); + + expression = parser.parseExpression(prefix + "3('abc',5.0f,1).output"); + assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:1"); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:1"); + + expression = parser.parseExpression(prefix + "3('abc',5.0f).output"); + assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:"); + assertCanCompile(expression); + assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:"); + + expression = parser.parseExpression(prefix + "4(#root).output"); + assertThat(expression.getValue(new int[] {1,2,3}, String.class)).isEqualTo("123"); + assertCanCompile(expression); + assertThat(expression.getValue(new int[] {1,2,3}, String.class)).isEqualTo("123"); + } + + @Test + void methodReferenceMissingCastAndRootObjectAccessing_SPR12326() { + // Need boxing code on the 1 so that toString() can be called + expression = parser.parseExpression("1.toString()"); + assertThat(expression.getValue()).isEqualTo("1"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo("1"); + + expression = parser.parseExpression("#it?.age.equals([0])"); + Person person = new Person(1); + StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {person.getAge()}); + context.setVariable("it", person); + assertThat(expression.getValue(context, Boolean.class)).isTrue(); + assertCanCompile(expression); + assertThat(expression.getValue(context, Boolean.class)).isTrue(); + + // Variant of above more like what was in the bug report: + SpelExpressionParser parser = new SpelExpressionParser( + new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, getClass().getClassLoader())); + + SpelExpression ex = parser.parseRaw("#it?.age.equals([0])"); + context = new StandardEvaluationContext(new Object[] {person.getAge()}); + context.setVariable("it", person); + assertThat(ex.getValue(context, Boolean.class)).isTrue(); + + PersonInOtherPackage person2 = new PersonInOtherPackage(1); + ex = parser.parseRaw("#it?.age.equals([0])"); + context = new StandardEvaluationContext(new Object[] {person2.getAge()}); + context.setVariable("it", person2); + assertThat(ex.getValue(context, Boolean.class)).isTrue(); + + ex = parser.parseRaw("#it?.age.equals([0])"); + context = new StandardEvaluationContext(new Object[] {person2.getAge()}); + context.setVariable("it", person2); + assertThat((Boolean) ex.getValue(context)).isTrue(); + } + + @Test + void constructorReference() { + // simple constructor + expression = parser.parseExpression("new String('123')"); + assertThat(expression.getValue()).isEqualTo("123"); + assertCanCompile(expression); + assertThat(expression.getValue()).isEqualTo("123"); + + String testclass8 = TestClass8.class.getName(); + Object result; + + // multi arg constructor that includes primitives + expression = parser.parseExpression("new " + testclass8 + "(42,'123',4.0d,true)"); + result = expression.getValue(); + assertThat(result).isExactlyInstanceOf(TestClass8.class); + assertCanCompile(expression); + result = expression.getValue(); + assertThat(result).isExactlyInstanceOf(TestClass8.class); + TestClass8 tc8 = (TestClass8) result; + assertThat(tc8.i).isEqualTo(42); + assertThat(tc8.s).isEqualTo("123"); + assertThat(tc8.d).isCloseTo(4.0d, within(0.5d)); + + assertThat(tc8.z).isTrue(); + + // no-arg constructor + expression = parser.parseExpression("new " + testclass8 + "()"); + result = expression.getValue(); + assertThat(result).isExactlyInstanceOf(TestClass8.class); + assertCanCompile(expression); + result = expression.getValue(); + assertThat(result).isExactlyInstanceOf(TestClass8.class); + + // pass primitive to reference type constructor + expression = parser.parseExpression("new " + testclass8 + "(42)"); + result = expression.getValue(); + assertThat(result).isExactlyInstanceOf(TestClass8.class); + assertCanCompile(expression); + result = expression.getValue(); + assertThat(result).isExactlyInstanceOf(TestClass8.class); + tc8 = (TestClass8) result; + assertThat(tc8.i).isEqualTo(42); + + // varargs + expression = parser.parseExpression("new " + testclass8 + "(#root)"); + Object[] objectArray = { "a", "b", "c" }; + result = expression.getValue(objectArray); + assertThat(result).isExactlyInstanceOf(TestClass8.class); + assertCanCompile(expression); + result = expression.getValue(objectArray); + assertThat(result).isExactlyInstanceOf(TestClass8.class); + tc8 = (TestClass8) result; + assertThat(tc8.args).containsExactly("a", "b", "c"); + + // varargs with argument component type that is a subtype of the varargs component type. + expression = parser.parseExpression("new " + testclass8 + "(#root)"); + String[] stringArray = { "a", "b", "c" }; + result = expression.getValue(stringArray); + assertThat(result).isExactlyInstanceOf(TestClass8.class); + assertCanCompile(expression); + result = expression.getValue(stringArray); + assertThat(result).isExactlyInstanceOf(TestClass8.class); + tc8 = (TestClass8) result; + assertThat(tc8.args).containsExactly("a", "b", "c"); + + // private class, can't compile it + String testclass9 = TestClass9.class.getName(); + expression = parser.parseExpression("new " + testclass9 + "(42)"); + result = expression.getValue(); + assertThat(result).isExactlyInstanceOf(TestClass9.class); + assertCannotCompile(expression); + } + + @Test + void methodReferenceReflectiveMethodSelectionWithVarargs() { + TestClass10 tc = new TestClass10(); + + // Should call the non varargs version of concat + // (which causes the '::' prefix in test output) + expression = parser.parseExpression("concat('test')"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("::test"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("::test"); + tc.reset(); + + // This will call the varargs concat with an empty array + expression = parser.parseExpression("concat()"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEmpty(); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEmpty(); + tc.reset(); + + // Should call the non varargs version of concat + // (which causes the '::' prefix in test output) + expression = parser.parseExpression("concat2('test')"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("::test"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("::test"); + tc.reset(); + + // This will call the varargs concat with an empty array + expression = parser.parseExpression("concat2()"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEmpty(); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEmpty(); + tc.reset(); + } + + @Test + void methodReferenceVarargs() { + TestClass5 tc = new TestClass5(); + + // varargs string + expression = parser.parseExpression("eleven()"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEmpty(); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEmpty(); + tc.reset(); + + // varargs string + expression = parser.parseExpression("eleven('aaa')"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaa"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaa"); + tc.reset(); + + // varargs string + expression = parser.parseExpression("eleven(stringArray)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaabbbccc"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaabbbccc"); + tc.reset(); + + // varargs string + expression = parser.parseExpression("eleven('aaa','bbb','ccc')"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaabbbccc"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaabbbccc"); + tc.reset(); + + // varargs object + expression = parser.parseExpression("sixteen('aaa','bbb','ccc')"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaabbbccc"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaabbbccc"); + tc.reset(); + + // string array from property in varargs object + expression = parser.parseExpression("sixteen(seventeen)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaabbbccc"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaabbbccc"); + tc.reset(); + + // string array from variable in varargs object + expression = parser.parseExpression("sixteen(stringArray)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaabbbccc"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaabbbccc"); + tc.reset(); + + // string array in varargs object with other parameter + expression = parser.parseExpression("eighteen('AAA', stringArray)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("AAA::aaabbbccc"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("AAA::aaabbbccc"); + tc.reset(); + + // varargs int + expression = parser.parseExpression("twelve(1,2,3)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.i).isEqualTo(6); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.i).isEqualTo(6); + tc.reset(); + + expression = parser.parseExpression("twelve(1)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.i).isEqualTo(1); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.i).isEqualTo(1); + tc.reset(); + + // one string then varargs string + expression = parser.parseExpression("thirteen('aaa','bbb','ccc')"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaa::bbbccc"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaa::bbbccc"); + tc.reset(); + + // nothing passed to varargs parameter + expression = parser.parseExpression("thirteen('aaa')"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaa::"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaa::"); + tc.reset(); + + // nested arrays + expression = parser.parseExpression("fourteen('aaa',stringArray,stringArray)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaa::{aaabbbccc}{aaabbbccc}"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaa::{aaabbbccc}{aaabbbccc}"); + tc.reset(); + + // nested primitive array + expression = parser.parseExpression("fifteen('aaa',intArray,intArray)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaa::{112233}{112233}"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("aaa::{112233}{112233}"); + tc.reset(); + + // varargs boolean + expression = parser.parseExpression("arrayz(true,true,false)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("truetruefalse"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("truetruefalse"); + tc.reset(); + + expression = parser.parseExpression("arrayz(true)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("true"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("true"); + tc.reset(); + + // varargs short + expression = parser.parseExpression("arrays(s1,s2,s3)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("123"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("123"); + tc.reset(); + + expression = parser.parseExpression("arrays(s1)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1"); + tc.reset(); + + // varargs double + expression = parser.parseExpression("arrayd(1.0d,2.0d,3.0d)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1.02.03.0"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1.02.03.0"); + tc.reset(); + + expression = parser.parseExpression("arrayd(1.0d)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1.0"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1.0"); + tc.reset(); + + // varargs long + expression = parser.parseExpression("arrayj(l1,l2,l3)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("123"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("123"); + tc.reset(); + + expression = parser.parseExpression("arrayj(l1)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1"); + tc.reset(); + + // varargs char + expression = parser.parseExpression("arrayc(c1,c2,c3)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("abc"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("abc"); + tc.reset(); + + expression = parser.parseExpression("arrayc(c1)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("a"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("a"); + tc.reset(); + + // varargs byte + expression = parser.parseExpression("arrayb(b1,b2,b3)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("656667"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("656667"); + tc.reset(); + + expression = parser.parseExpression("arrayb(b1)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("65"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("65"); + tc.reset(); + + // varargs float + expression = parser.parseExpression("arrayf(f1,f2,f3)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1.02.03.0"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1.02.03.0"); + tc.reset(); + + expression = parser.parseExpression("arrayf(f1)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1.0"); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("1.0"); + tc.reset(); + } + + @Test + void methodReference() { + TestClass5 tc = new TestClass5(); + + // non-static method, no args, void return + expression = parser.parseExpression("one()"); + assertCannotCompile(expression); + expression.getValue(tc); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.i).isEqualTo(1); + tc.reset(); + + // static method, no args, void return + expression = parser.parseExpression("two()"); + assertCannotCompile(expression); + expression.getValue(tc); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(TestClass5._i).isEqualTo(1); + tc.reset(); + + // non-static method, reference type return + expression = parser.parseExpression("three()"); + assertCannotCompile(expression); + expression.getValue(tc); + assertCanCompile(expression); + tc.reset(); + assertThat(expression.getValue(tc)).isEqualTo("hello"); + tc.reset(); + + // non-static method, primitive type return + expression = parser.parseExpression("four()"); + assertCannotCompile(expression); + expression.getValue(tc); + assertCanCompile(expression); + tc.reset(); + assertThat(expression.getValue(tc)).isEqualTo(3277700L); + tc.reset(); + + // static method, reference type return + expression = parser.parseExpression("five()"); + assertCannotCompile(expression); + expression.getValue(tc); + assertCanCompile(expression); + tc.reset(); + assertThat(expression.getValue(tc)).isEqualTo("hello"); + tc.reset(); + + // static method, primitive type return + expression = parser.parseExpression("six()"); + assertCannotCompile(expression); + expression.getValue(tc); + assertCanCompile(expression); + tc.reset(); + assertThat(expression.getValue(tc)).isEqualTo(3277700L); + tc.reset(); + + // non-static method, one parameter of reference type + expression = parser.parseExpression("seven(\"foo\")"); + assertCannotCompile(expression); + expression.getValue(tc); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("foo"); + tc.reset(); + + // static method, one parameter of reference type + expression = parser.parseExpression("eight(\"bar\")"); + assertCannotCompile(expression); + expression.getValue(tc); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(TestClass5._s).isEqualTo("bar"); + tc.reset(); + + // non-static method, one parameter of primitive type + expression = parser.parseExpression("nine(231)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(tc.i).isEqualTo(231); + tc.reset(); + + // static method, one parameter of primitive type + expression = parser.parseExpression("ten(111)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertCanCompile(expression); + tc.reset(); + expression.getValue(tc); + assertThat(TestClass5._i).isEqualTo(111); + tc.reset(); + + // method that gets type converted parameters + + // Converting from an int to a string + expression = parser.parseExpression("seven(123)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("123"); + assertCannotCompile(expression); // Uncompilable as argument conversion is occurring + + Expression expression = parser.parseExpression("'abcd'.substring(index1,index2)"); + String interpretedResult = expression.getValue(new TestClass1(), String.class); + assertCanCompile(expression); + String compiledResult = expression.getValue(new TestClass1(), String.class); + assertThat(interpretedResult).isEqualTo("bc"); + assertThat(compiledResult).isEqualTo("bc"); + + // Converting from an int to a Number + expression = parser.parseExpression("takeNumber(123)"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("123"); + tc.reset(); + assertCanCompile(expression); // The generated code should include boxing of the int to a Number + expression.getValue(tc); + assertThat(tc.s).isEqualTo("123"); + + // Passing a subtype + expression = parser.parseExpression("takeNumber(T(Integer).valueOf(42))"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("42"); + tc.reset(); + assertCanCompile(expression); // The generated code should include boxing of the int to a Number + expression.getValue(tc); + assertThat(tc.s).isEqualTo("42"); + + // Passing a subtype + expression = parser.parseExpression("takeString(T(Integer).valueOf(42))"); + assertCannotCompile(expression); + expression.getValue(tc); + assertThat(tc.s).isEqualTo("42"); + tc.reset(); + assertCannotCompile(expression); // method takes a string and we are passing an Integer + } + + @Test + void methodReference_staticMethod() { + Expression expression = parser.parseExpression("T(Integer).valueOf(42)"); + int interpretedResult = expression.getValue(new TestClass1(), int.class); + assertCanCompile(expression); + int compiledResult = expression.getValue(new TestClass1(), int.class); + assertThat(interpretedResult).isEqualTo(42); + assertThat(compiledResult).isEqualTo(42); + } + + @Test + void methodReference_literalArguments_int() { + Expression expression = parser.parseExpression("'abcd'.substring(1,3)"); + String interpretedResult = expression.getValue(new TestClass1(), String.class); + assertCanCompile(expression); + String compiledResult = expression.getValue(new TestClass1(), String.class); + assertThat(interpretedResult).isEqualTo("bc"); + assertThat(compiledResult).isEqualTo("bc"); + } + + @Test + void methodReference_simpleInstanceMethodNoArg() { + Expression expression = parser.parseExpression("toString()"); + String interpretedResult = expression.getValue(42, String.class); + assertCanCompile(expression); + String compiledResult = expression.getValue(42, String.class); + assertThat(interpretedResult).isEqualTo("42"); + assertThat(compiledResult).isEqualTo("42"); + } + + @Test + void methodReference_simpleInstanceMethodNoArgReturnPrimitive() { + expression = parser.parseExpression("intValue()"); + int interpretedResult = expression.getValue(42, int.class); + assertThat(interpretedResult).isEqualTo(42); + assertCanCompile(expression); + int compiledResult = expression.getValue(42, int.class); + assertThat(compiledResult).isEqualTo(42); + } + + @Test + void methodReference_simpleInstanceMethodOneArgReturnPrimitive1() { + Expression expression = parser.parseExpression("indexOf('b')"); + int interpretedResult = expression.getValue("abc", int.class); + assertCanCompile(expression); + int compiledResult = expression.getValue("abc", int.class); + assertThat(interpretedResult).isEqualTo(1); + assertThat(compiledResult).isEqualTo(1); + } + + @Test + void methodReference_simpleInstanceMethodOneArgReturnPrimitive2() { + expression = parser.parseExpression("charAt(2)"); + char interpretedResult = expression.getValue("abc", char.class); + assertThat(interpretedResult).isEqualTo('c'); + assertCanCompile(expression); + char compiledResult = expression.getValue("abc", char.class); + assertThat(compiledResult).isEqualTo('c'); + } + } + + @Nested + class PropertyAccessTests { + + @Test + void propertyReference() { + TestClass6 tc = new TestClass6(); + + // non-static field + expression = parser.parseExpression("orange"); + assertCannotCompile(expression); + assertThat(expression.getValue(tc)).isEqualTo("value1"); + assertCanCompile(expression); + assertThat(expression.getValue(tc)).isEqualTo("value1"); + + // static field + expression = parser.parseExpression("apple"); + assertCannotCompile(expression); + assertThat(expression.getValue(tc)).isEqualTo("value2"); + assertCanCompile(expression); + assertThat(expression.getValue(tc)).isEqualTo("value2"); + + // non static getter + expression = parser.parseExpression("banana"); + assertCannotCompile(expression); + assertThat(expression.getValue(tc)).isEqualTo("value3"); + assertCanCompile(expression); + assertThat(expression.getValue(tc)).isEqualTo("value3"); + + // static getter + expression = parser.parseExpression("plum"); + assertCannotCompile(expression); + assertThat(expression.getValue(tc)).isEqualTo("value4"); + assertCanCompile(expression); + assertThat(expression.getValue(tc)).isEqualTo("value4"); + + // record-style accessor + expression = parser.parseExpression("strawberry"); + assertCannotCompile(expression); + assertThat(expression.getValue(tc)).isEqualTo("value5"); + assertCanCompile(expression); + assertThat(expression.getValue(tc)).isEqualTo("value5"); + } + + @Test + void propertyReferenceVisibility_SPR12771() { + StandardEvaluationContext ctx = new StandardEvaluationContext(); + ctx.setVariable("httpServletRequest", HttpServlet3RequestFactory.getOne()); + // Without a fix compilation was inserting a checkcast to a private type + expression = parser.parseExpression("#httpServletRequest.servletPath"); + assertThat(expression.getValue(ctx)).isEqualTo("wibble"); + assertCanCompile(expression); + assertThat(expression.getValue(ctx)).isEqualTo("wibble"); + } + + @Nested + class PropertyVisibilityTests { + + @Test + void privateSubclassOverridesPropertyInPublicInterface() { + expression = parser.parseExpression("text"); + PrivateSubclass privateSubclass = new PrivateSubclass(); + + // Prerequisite: type must not be public for this use case. + assertNotPublic(privateSubclass.getClass()); + + String result = expression.getValue(context, privateSubclass, String.class); + assertThat(result).isEqualTo("enigma"); + + assertCanCompile(expression); + result = expression.getValue(context, privateSubclass, String.class); + assertThat(result).isEqualTo("enigma"); + } + + @Test + void privateSubclassOverridesPropertyInPrivateInterface() { + expression = parser.parseExpression("message"); + PrivateSubclass privateSubclass = new PrivateSubclass(); + + // Prerequisite: type must not be public for this use case. + assertNotPublic(privateSubclass.getClass()); + + String result = expression.getValue(context, privateSubclass, String.class); + assertThat(result).isEqualTo("hello"); + + assertCanCompile(expression); + result = expression.getValue(context, privateSubclass, String.class); + assertThat(result).isEqualTo("hello"); + } + + @Test + void privateSubclassOverridesPropertyInPublicSuperclass() { + expression = parser.parseExpression("number"); + PrivateSubclass privateSubclass = new PrivateSubclass(); + + // Prerequisite: type must not be public for this use case. + assertNotPublic(privateSubclass.getClass()); + + Integer result = expression.getValue(context, privateSubclass, Integer.class); + assertThat(result).isEqualTo(2); + + assertCanCompile(expression); + result = expression.getValue(context, privateSubclass, Integer.class); + assertThat(result).isEqualTo(2); + } + + @Test + void indexIntoPropertyInPrivateSubclassThatOverridesPropertyInPublicInterface() { + expression = parser.parseExpression("#root['text']"); + PrivateSubclass privateSubclass = new PrivateSubclass(); + + // Prerequisite: type must not be public for this use case. + assertNotPublic(privateSubclass.getClass()); + + String result = expression.getValue(context, privateSubclass, String.class); + assertThat(result).isEqualTo("enigma"); + + assertCanCompile(expression); + result = expression.getValue(context, privateSubclass, String.class); + assertThat(result).isEqualTo("enigma"); + } + + @Test + void indexIntoPropertyInPrivateSubclassThatOverridesPropertyInPrivateInterface() { + expression = parser.parseExpression("#root['message']"); + PrivateSubclass privateSubclass = new PrivateSubclass(); + + // Prerequisite: type must not be public for this use case. + assertNotPublic(privateSubclass.getClass()); + + String result = expression.getValue(context, privateSubclass, String.class); + assertThat(result).isEqualTo("hello"); + + assertCanCompile(expression); + result = expression.getValue(context, privateSubclass, String.class); + assertThat(result).isEqualTo("hello"); + } + + @Test + void indexIntoPropertyInPrivateSubclassThatOverridesPropertyInPublicSuperclass() { + expression = parser.parseExpression("#root['number']"); + PrivateSubclass privateSubclass = new PrivateSubclass(); + + // Prerequisite: type must not be public for this use case. + assertNotPublic(privateSubclass.getClass()); + + Integer result = expression.getValue(context, privateSubclass, Integer.class); + assertThat(result).isEqualTo(2); + + assertCanCompile(expression); + result = expression.getValue(context, privateSubclass, Integer.class); + assertThat(result).isEqualTo(2); + } + + } } @Nested @@ -560,6 +5217,36 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object"); } + @Test + void indexIntoMap_SPR12045() { + SpelParserConfiguration config = new SpelParserConfiguration( + SpelCompilerMode.IMMEDIATE,getClass().getClassLoader()); + SpelExpressionParser parser = new SpelExpressionParser(config); + expression = parser.parseExpression("headers[command]"); + + MyMessage root = new MyMessage(); + assertThat(expression.getValue(root)).isEqualTo("wibble"); + // This next call was failing because the isCompilable check in Indexer + // did not check on the key being compilable (and also generateCode in the + // Indexer was missing the optimization that it didn't need necessarily + // need to call generateCode for that accessor) + assertThat(expression.getValue(root)).isEqualTo("wibble"); + assertCanCompile(expression); + + // What about a map key that is an expression - ensure the getKey() is evaluated in the right scope + expression = parser.parseExpression("headers[getKey()]"); + assertThat(expression.getValue(root)).isEqualTo("wobble"); + assertThat(expression.getValue(root)).isEqualTo("wobble"); + + expression = parser.parseExpression("list[getKey2()]"); + assertThat(expression.getValue(root)).isEqualTo("wobble"); + assertThat(expression.getValue(root)).isEqualTo("wobble"); + + expression = parser.parseExpression("ia[getKey2()]"); + assertThat(expression.getValue(root)).isEqualTo(3); + assertThat(expression.getValue(root)).isEqualTo(3); + } + @Test void indexIntoMapOfListOfString() { Map> map = Map.of("foo", List.of("a", "b", "c")); @@ -1279,7 +5966,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } @Nested - class TernaryTests { + class TernaryOperatorTests { @Test void ternaryWithTrueConditionAndLiteralsOfSameType() { @@ -1607,106 +6294,6 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } } - @Nested - class PropertyVisibilityTests { - - @Test - void privateSubclassOverridesPropertyInPublicInterface() { - expression = parser.parseExpression("text"); - PrivateSubclass privateSubclass = new PrivateSubclass(); - - // Prerequisite: type must not be public for this use case. - assertNotPublic(privateSubclass.getClass()); - - String result = expression.getValue(context, privateSubclass, String.class); - assertThat(result).isEqualTo("enigma"); - - assertCanCompile(expression); - result = expression.getValue(context, privateSubclass, String.class); - assertThat(result).isEqualTo("enigma"); - } - - @Test - void privateSubclassOverridesPropertyInPrivateInterface() { - expression = parser.parseExpression("message"); - PrivateSubclass privateSubclass = new PrivateSubclass(); - - // Prerequisite: type must not be public for this use case. - assertNotPublic(privateSubclass.getClass()); - - String result = expression.getValue(context, privateSubclass, String.class); - assertThat(result).isEqualTo("hello"); - - assertCanCompile(expression); - result = expression.getValue(context, privateSubclass, String.class); - assertThat(result).isEqualTo("hello"); - } - - @Test - void privateSubclassOverridesPropertyInPublicSuperclass() { - expression = parser.parseExpression("number"); - PrivateSubclass privateSubclass = new PrivateSubclass(); - - // Prerequisite: type must not be public for this use case. - assertNotPublic(privateSubclass.getClass()); - - Integer result = expression.getValue(context, privateSubclass, Integer.class); - assertThat(result).isEqualTo(2); - - assertCanCompile(expression); - result = expression.getValue(context, privateSubclass, Integer.class); - assertThat(result).isEqualTo(2); - } - - @Test - void indexIntoPropertyInPrivateSubclassThatOverridesPropertyInPublicInterface() { - expression = parser.parseExpression("#root['text']"); - PrivateSubclass privateSubclass = new PrivateSubclass(); - - // Prerequisite: type must not be public for this use case. - assertNotPublic(privateSubclass.getClass()); - - String result = expression.getValue(context, privateSubclass, String.class); - assertThat(result).isEqualTo("enigma"); - - assertCanCompile(expression); - result = expression.getValue(context, privateSubclass, String.class); - assertThat(result).isEqualTo("enigma"); - } - - @Test - void indexIntoPropertyInPrivateSubclassThatOverridesPropertyInPrivateInterface() { - expression = parser.parseExpression("#root['message']"); - PrivateSubclass privateSubclass = new PrivateSubclass(); - - // Prerequisite: type must not be public for this use case. - assertNotPublic(privateSubclass.getClass()); - - String result = expression.getValue(context, privateSubclass, String.class); - assertThat(result).isEqualTo("hello"); - - assertCanCompile(expression); - result = expression.getValue(context, privateSubclass, String.class); - assertThat(result).isEqualTo("hello"); - } - - @Test - void indexIntoPropertyInPrivateSubclassThatOverridesPropertyInPublicSuperclass() { - expression = parser.parseExpression("#root['number']"); - PrivateSubclass privateSubclass = new PrivateSubclass(); - - // Prerequisite: type must not be public for this use case. - assertNotPublic(privateSubclass.getClass()); - - Integer result = expression.getValue(context, privateSubclass, Integer.class); - assertThat(result).isEqualTo(2); - - assertCanCompile(expression); - result = expression.getValue(context, privateSubclass, Integer.class); - assertThat(result).isEqualTo(2); - } - } - @Nested class MethodVisibilityTests { @@ -1841,4583 +6428,6 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } - @Test - void typeReference() { - expression = parse("T(String)"); - assertThat(expression.getValue()).isEqualTo(String.class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(String.class); - - expression = parse("T(java.io.IOException)"); - assertThat(expression.getValue()).isEqualTo(IOException.class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(IOException.class); - - expression = parse("T(java.io.IOException[])"); - assertThat(expression.getValue()).isEqualTo(IOException[].class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(IOException[].class); - - expression = parse("T(int[][])"); - assertThat(expression.getValue()).isEqualTo(int[][].class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(int[][].class); - - expression = parse("T(int)"); - assertThat(expression.getValue()).isEqualTo(int.class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(int.class); - - expression = parse("T(byte)"); - assertThat(expression.getValue()).isEqualTo(byte.class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(byte.class); - - expression = parse("T(char)"); - assertThat(expression.getValue()).isEqualTo(char.class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(char.class); - - expression = parse("T(short)"); - assertThat(expression.getValue()).isEqualTo(short.class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(short.class); - - expression = parse("T(long)"); - assertThat(expression.getValue()).isEqualTo(long.class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(long.class); - - expression = parse("T(float)"); - assertThat(expression.getValue()).isEqualTo(float.class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(float.class); - - expression = parse("T(double)"); - assertThat(expression.getValue()).isEqualTo(double.class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(double.class); - - expression = parse("T(boolean)"); - assertThat(expression.getValue()).isEqualTo(boolean.class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(boolean.class); - - expression = parse("T(Missing)"); - assertThatExceptionOfType(SpelEvaluationException.class) - .isThrownBy(expression::getValue) - .withMessageEndingWith("Type cannot be found 'Missing'"); - assertCannotCompile(expression); - } - - @SuppressWarnings("unchecked") - @Test - void operatorInstanceOf() { - expression = parse("'xyz' instanceof T(String)"); - assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); - - expression = parse("'xyz' instanceof T(Integer)"); - assertThat(expression.getValue()).isEqualTo(false); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(false); - - List list = new ArrayList<>(); - expression = parse("#root instanceof T(java.util.List)"); - assertThat(expression.getValue(list)).asInstanceOf(BOOLEAN).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(list)).asInstanceOf(BOOLEAN).isTrue(); - - List[] arrayOfLists = new List[] {new ArrayList()}; - expression = parse("#root instanceof T(java.util.List[])"); - assertThat(expression.getValue(arrayOfLists)).asInstanceOf(BOOLEAN).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(arrayOfLists)).asInstanceOf(BOOLEAN).isTrue(); - - int[] intArray = new int[] {1,2,3}; - expression = parse("#root instanceof T(int[])"); - assertThat(expression.getValue(intArray)).asInstanceOf(BOOLEAN).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(intArray)).asInstanceOf(BOOLEAN).isTrue(); - - String root = null; - expression = parse("#root instanceof T(Integer)"); - assertThat(expression.getValue(root)).isEqualTo(false); - assertCanCompile(expression); - assertThat(expression.getValue(root)).isEqualTo(false); - - // root still null - expression = parse("#root instanceof T(java.lang.Object)"); - assertThat(expression.getValue(root)).isEqualTo(false); - assertCanCompile(expression); - assertThat(expression.getValue(root)).isEqualTo(false); - - root = "howdy!"; - expression = parse("#root instanceof T(java.lang.Object)"); - assertThat(expression.getValue(root)).asInstanceOf(BOOLEAN).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(root)).asInstanceOf(BOOLEAN).isTrue(); - } - - @Test - void operatorInstanceOf_SPR14250() { - // primitive left operand - should get boxed, return true - expression = parse("3 instanceof T(Integer)"); - assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); - - // primitive left operand - should get boxed, return false - expression = parse("3 instanceof T(String)"); - assertThat(expression.getValue()).isEqualTo(false); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(false); - - // double slot left operand - should get boxed, return false - expression = parse("3.0d instanceof T(Integer)"); - assertThat(expression.getValue()).isEqualTo(false); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(false); - - // double slot left operand - should get boxed, return true - expression = parse("3.0d instanceof T(Double)"); - assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); - - // Only when the right-hand operand is a direct type reference - // will it be compilable. - StandardEvaluationContext ctx = new StandardEvaluationContext(); - ctx.setVariable("foo", String.class); - expression = parse("3 instanceof #foo"); - assertThat(expression.getValue(ctx)).isEqualTo(false); - assertCannotCompile(expression); - - // use of primitive as type for instanceof check - compilable - // but always false - expression = parse("3 instanceof T(int)"); - assertThat(expression.getValue()).isEqualTo(false); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(false); - - expression = parse("3 instanceof T(long)"); - assertThat(expression.getValue()).isEqualTo(false); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(false); - } - - @Test - void stringLiteral() { - expression = parser.parseExpression("'abcde'"); - assertThat(expression.getValue(new TestClass1(), String.class)).isEqualTo("abcde"); - assertCanCompile(expression); - String resultC = expression.getValue(new TestClass1(), String.class); - assertThat(resultC).isEqualTo("abcde"); - assertThat(expression.getValue(String.class)).isEqualTo("abcde"); - assertThat(expression.getValue()).isEqualTo("abcde"); - assertThat(expression.getValue(new StandardEvaluationContext())).isEqualTo("abcde"); - expression = parser.parseExpression("\"abcde\""); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("abcde"); - } - - @Test - void nullLiteral() { - expression = parser.parseExpression("null"); - Object resultI = expression.getValue(new TestClass1(), Object.class); - assertCanCompile(expression); - Object resultC = expression.getValue(new TestClass1(), Object.class); - assertThat(resultI).isNull(); - assertThat(resultC).isNull(); - assertThat(resultC).isNull(); - } - - @Test - void realLiteral() { - expression = parser.parseExpression("3.4d"); - double resultI = expression.getValue(new TestClass1(), double.class); - assertCanCompile(expression); - double resultC = expression.getValue(new TestClass1(), double.class); - assertThat(resultI).isCloseTo(3.4d, within(0.1d)); - - assertThat(resultC).isCloseTo(3.4d, within(0.1d)); - - assertThat(expression.getValue()).isEqualTo(3.4d); - } - - @SuppressWarnings("rawtypes") - @Test - void inlineList() { - expression = parser.parseExpression("'abcde'.substring({1,3,4}[0])"); - Object o = expression.getValue(); - assertThat(o).isEqualTo("bcde"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo("bcde"); - - expression = parser.parseExpression("{'abc','def'}"); - List l = (List) expression.getValue(); - assertThat(l.toString()).isEqualTo("[abc, def]"); - assertCanCompile(expression); - l = (List) expression.getValue(); - assertThat(l.toString()).isEqualTo("[abc, def]"); - - expression = parser.parseExpression("{'abc','def'}[0]"); - o = expression.getValue(); - assertThat(o).isEqualTo("abc"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo("abc"); - - expression = parser.parseExpression("{'abcde','ijklm'}[0].substring({1,3,4}[0])"); - o = expression.getValue(); - assertThat(o).isEqualTo("bcde"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo("bcde"); - - expression = parser.parseExpression("{'abcde','ijklm'}[0].substring({1,3,4}[0],{1,3,4}[1])"); - o = expression.getValue(); - assertThat(o).isEqualTo("bc"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo("bc"); - } - - @SuppressWarnings("rawtypes") - @Test - void nestedInlineLists() { - Object o = null; - - expression = parser.parseExpression("{{1,2,3},{4,5,6},{7,8,9}}"); - o = expression.getValue(); - assertThat(o.toString()).isEqualTo("[[1, 2, 3], [4, 5, 6], [7, 8, 9]]"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o.toString()).isEqualTo("[[1, 2, 3], [4, 5, 6], [7, 8, 9]]"); - - expression = parser.parseExpression("{{1,2,3},{4,5,6},{7,8,9}}.toString()"); - o = expression.getValue(); - assertThat(o).isEqualTo("[[1, 2, 3], [4, 5, 6], [7, 8, 9]]"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo("[[1, 2, 3], [4, 5, 6], [7, 8, 9]]"); - - expression = parser.parseExpression("{{1,2,3},{4,5,6},{7,8,9}}[1][0]"); - o = expression.getValue(); - assertThat(o).isEqualTo(4); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo(4); - - expression = parser.parseExpression("{{1,2,3},'abc',{7,8,9}}[1]"); - o = expression.getValue(); - assertThat(o).isEqualTo("abc"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo("abc"); - - expression = parser.parseExpression("'abcde'.substring({{1,3},1,3,4}[0][1])"); - o = expression.getValue(); - assertThat(o).isEqualTo("de"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo("de"); - - expression = parser.parseExpression("'abcde'.substring({{1,3},1,3,4}[1])"); - o = expression.getValue(); - assertThat(o).isEqualTo("bcde"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo("bcde"); - - expression = parser.parseExpression("{'abc',{'def','ghi'}}"); - List l = (List) expression.getValue(); - assertThat(l.toString()).isEqualTo("[abc, [def, ghi]]"); - assertCanCompile(expression); - l = (List) expression.getValue(); - assertThat(l.toString()).isEqualTo("[abc, [def, ghi]]"); - - expression = parser.parseExpression("{'abcde',{'ijklm','nopqr'}}[0].substring({1,3,4}[0])"); - o = expression.getValue(); - assertThat(o).isEqualTo("bcde"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo("bcde"); - - expression = parser.parseExpression("{'abcde',{'ijklm','nopqr'}}[1][0].substring({1,3,4}[0])"); - o = expression.getValue(); - assertThat(o).isEqualTo("jklm"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo("jklm"); - - expression = parser.parseExpression("{'abcde',{'ijklm','nopqr'}}[1][1].substring({1,3,4}[0],{1,3,4}[1])"); - o = expression.getValue(); - assertThat(o).isEqualTo("op"); - assertCanCompile(expression); - o = expression.getValue(); - assertThat(o).isEqualTo("op"); - } - - @Test - void intLiteral() { - expression = parser.parseExpression("42"); - int resultI = expression.getValue(new TestClass1(), int.class); - assertCanCompile(expression); - int resultC = expression.getValue(new TestClass1(), int.class); - assertThat(resultI).isEqualTo(42); - assertThat(resultC).isEqualTo(42); - - expression = parser.parseExpression("T(Integer).valueOf(42)"); - expression.getValue(Integer.class); - assertCanCompile(expression); - assertThat(expression.getValue(Integer.class)).isEqualTo(42); - - // Code gen is different for -1 .. 6 because there are bytecode instructions specifically for those values - - // Not an int literal but an opMinus with one operand: - expression = parser.parseExpression("-1"); - expression.getValue(Integer.class); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(-1); - - expression = parser.parseExpression("0"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(0); - - expression = parser.parseExpression("2"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(2); - - expression = parser.parseExpression("7"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(7); - } - - @Test - void longLiteral() { - expression = parser.parseExpression("99L"); - long resultI = expression.getValue(new TestClass1(), long.class); - assertCanCompile(expression); - long resultC = expression.getValue(new TestClass1(), long.class); - assertThat(resultI).isEqualTo(99L); - assertThat(resultC).isEqualTo(99L); - } - - @Test - void booleanLiteral() { - expression = parser.parseExpression("true"); - boolean resultI = expression.getValue(1, boolean.class); - assertThat(resultI).isTrue(); - assertThat(SpelCompiler.compile(expression)).isTrue(); - boolean resultC = expression.getValue(1, boolean.class); - assertThat(resultC).isTrue(); - - expression = parser.parseExpression("false"); - resultI = expression.getValue(1, boolean.class); - assertThat(resultI).isFalse(); - assertThat(SpelCompiler.compile(expression)).isTrue(); - resultC = expression.getValue(1, boolean.class); - assertThat(resultC).isFalse(); - } - - @Test - void floatLiteral() { - expression = parser.parseExpression("3.4f"); - float resultI = expression.getValue(new TestClass1(), float.class); - assertCanCompile(expression); - float resultC = expression.getValue(new TestClass1(), float.class); - assertThat(resultI).isCloseTo(3.4f, within(0.1f)); - - assertThat(resultC).isCloseTo(3.4f, within(0.1f)); - - assertThat(expression.getValue()).isEqualTo(3.4f); - } - - @Test - void opOr() { - Expression expression = parser.parseExpression("false or false"); - boolean resultI = expression.getValue(1, boolean.class); - SpelCompiler.compile(expression); - boolean resultC = expression.getValue(1, boolean.class); - assertThat(resultI).isFalse(); - assertThat(resultC).isFalse(); - - expression = parser.parseExpression("false or true"); - resultI = expression.getValue(1, boolean.class); - assertCanCompile(expression); - resultC = expression.getValue(1, boolean.class); - assertThat(resultI).isTrue(); - assertThat(resultC).isTrue(); - - expression = parser.parseExpression("true or false"); - resultI = expression.getValue(1, boolean.class); - assertCanCompile(expression); - resultC = expression.getValue(1, boolean.class); - assertThat(resultI).isTrue(); - assertThat(resultC).isTrue(); - - expression = parser.parseExpression("true or true"); - resultI = expression.getValue(1, boolean.class); - assertCanCompile(expression); - resultC = expression.getValue(1, boolean.class); - assertThat(resultI).isTrue(); - assertThat(resultC).isTrue(); - - TestClass4 tc = new TestClass4(); - expression = parser.parseExpression("getfalse() or gettrue()"); - resultI = expression.getValue(tc, boolean.class); - assertCanCompile(expression); - resultC = expression.getValue(tc, boolean.class); - assertThat(resultI).isTrue(); - assertThat(resultC).isTrue(); - - // Can't compile this as we aren't going down the getfalse() branch in our evaluation - expression = parser.parseExpression("gettrue() or getfalse()"); - resultI = expression.getValue(tc, boolean.class); - assertCannotCompile(expression); - - expression = parser.parseExpression("getA() or getB()"); - tc.a = true; - tc.b = true; - resultI = expression.getValue(tc, boolean.class); - assertCannotCompile(expression); // Haven't yet been into second branch - tc.a = false; - tc.b = true; - resultI = expression.getValue(tc, boolean.class); - assertCanCompile(expression); // Now been down both - assertThat(resultI).isTrue(); - - boolean b = false; - expression = parse("#root or #root"); - Object resultI2 = expression.getValue(b); - assertCanCompile(expression); - assertThat((Boolean) resultI2).isFalse(); - assertThat((Boolean) expression.getValue(b)).isFalse(); - } - - @Test - void opAnd() { - Expression expression = parser.parseExpression("false and false"); - boolean resultI = expression.getValue(1, boolean.class); - SpelCompiler.compile(expression); - boolean resultC = expression.getValue(1, boolean.class); - assertThat(resultI).isFalse(); - assertThat(resultC).isFalse(); - - expression = parser.parseExpression("false and true"); - resultI = expression.getValue(1, boolean.class); - SpelCompiler.compile(expression); - resultC = expression.getValue(1, boolean.class); - assertThat(resultI).isFalse(); - assertThat(resultC).isFalse(); - - expression = parser.parseExpression("true and false"); - resultI = expression.getValue(1, boolean.class); - SpelCompiler.compile(expression); - resultC = expression.getValue(1, boolean.class); - assertThat(resultI).isFalse(); - assertThat(resultC).isFalse(); - - expression = parser.parseExpression("true and true"); - resultI = expression.getValue(1, boolean.class); - SpelCompiler.compile(expression); - resultC = expression.getValue(1, boolean.class); - assertThat(resultI).isTrue(); - assertThat(resultC).isTrue(); - - TestClass4 tc = new TestClass4(); - - // Can't compile this as we aren't going down the gettrue() branch in our evaluation - expression = parser.parseExpression("getfalse() and gettrue()"); - resultI = expression.getValue(tc, boolean.class); - assertCannotCompile(expression); - - expression = parser.parseExpression("getA() and getB()"); - tc.a = false; - tc.b = false; - resultI = expression.getValue(tc, boolean.class); - assertCannotCompile(expression); // Haven't yet been into second branch - tc.a = true; - tc.b = false; - resultI = expression.getValue(tc, boolean.class); - assertCanCompile(expression); // Now been down both - assertThat(resultI).isFalse(); - tc.a = true; - tc.b = true; - resultI = expression.getValue(tc, boolean.class); - assertThat(resultI).isTrue(); - - boolean b = true; - expression = parse("#root and #root"); - Object resultI2 = expression.getValue(b); - assertCanCompile(expression); - assertThat((Boolean) resultI2).isTrue(); - assertThat((Boolean) expression.getValue(b)).isTrue(); - } - - @Test - void operatorNot() { - expression = parse("!true"); - assertThat(expression.getValue()).isEqualTo(false); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(false); - - expression = parse("!false"); - assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue()).asInstanceOf(BOOLEAN).isTrue(); - - boolean b = true; - expression = parse("!#root"); - assertThat(expression.getValue(b)).isEqualTo(false); - assertCanCompile(expression); - assertThat(expression.getValue(b)).isEqualTo(false); - - b = false; - expression = parse("!#root"); - assertThat(expression.getValue(b)).asInstanceOf(BOOLEAN).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(b)).asInstanceOf(BOOLEAN).isTrue(); - } - - @Test - void nullsafeFieldPropertyDereferencing_SPR16489() { - FooObjectHolder foh = new FooObjectHolder(); - StandardEvaluationContext context = new StandardEvaluationContext(); - context.setRootObject(foh); - - // First non compiled: - SpelExpression expression = (SpelExpression) parser.parseExpression("foo?.object"); - assertThat(expression.getValue(context)).isEqualTo("hello"); - foh.foo = null; - assertThat(expression.getValue(context)).isNull(); - - // Now revert state of foh and try compiling it: - foh.foo = new FooObject(); - assertThat(expression.getValue(context)).isEqualTo("hello"); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo("hello"); - foh.foo = null; - assertThat(expression.getValue(context)).isNull(); - - // Static references - expression = (SpelExpression) parser.parseExpression("#var?.propertya"); - context.setVariable("var", StaticsHelper.class); - assertThat(expression.getValue(context).toString()).isEqualTo("sh"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - assertCanCompile(expression); - context.setVariable("var", StaticsHelper.class); - assertThat(expression.getValue(context).toString()).isEqualTo("sh"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - - // Single size primitive (boolean) - expression = (SpelExpression) parser.parseExpression("#var?.a"); - context.setVariable("var", new TestClass4()); - assertThat((Boolean) expression.getValue(context)).isFalse(); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - assertCanCompile(expression); - context.setVariable("var", new TestClass4()); - assertThat((Boolean) expression.getValue(context)).isFalse(); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - - // Double slot primitives - expression = (SpelExpression) parser.parseExpression("#var?.four"); - context.setVariable("var", new Three()); - assertThat(expression.getValue(context).toString()).isEqualTo("0.04"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - assertCanCompile(expression); - context.setVariable("var", new Three()); - assertThat(expression.getValue(context).toString()).isEqualTo("0.04"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - } - - @Test // gh-27421 - void nullSafeMethodChainingWithNonStaticVoidMethod() { - FooObjectHolder foh = new FooObjectHolder(); - StandardEvaluationContext context = new StandardEvaluationContext(foh); - SpelExpression expression = (SpelExpression) parser.parseExpression("getFoo()?.doFoo()"); - - FooObject.doFooInvoked = false; - assertThat(expression.getValue(context)).isNull(); - assertThat(FooObject.doFooInvoked).isTrue(); - - FooObject.doFooInvoked = false; - foh.foo = null; - assertThat(expression.getValue(context)).isNull(); - assertThat(FooObject.doFooInvoked).isFalse(); - - assertCanCompile(expression); - - FooObject.doFooInvoked = false; - foh.foo = new FooObject(); - assertThat(expression.getValue(context)).isNull(); - assertThat(FooObject.doFooInvoked).isTrue(); - - FooObject.doFooInvoked = false; - foh.foo = null; - assertThat(expression.getValue(context)).isNull(); - assertThat(FooObject.doFooInvoked).isFalse(); - } - - @Test - void nullsafeMethodChaining_SPR16489() { - FooObjectHolder foh = new FooObjectHolder(); - StandardEvaluationContext context = new StandardEvaluationContext(); - context.setRootObject(foh); - - // First non compiled: - SpelExpression expression = (SpelExpression) parser.parseExpression("getFoo()?.getObject()"); - assertThat(expression.getValue(context)).isEqualTo("hello"); - foh.foo = null; - assertThat(expression.getValue(context)).isNull(); - assertCanCompile(expression); - foh.foo = new FooObject(); - assertThat(expression.getValue(context)).isEqualTo("hello"); - foh.foo = null; - assertThat(expression.getValue(context)).isNull(); - - // Static method references - expression = (SpelExpression) parser.parseExpression("#var?.methoda()"); - context.setVariable("var", StaticsHelper.class); - assertThat(expression.getValue(context).toString()).isEqualTo("sh"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - assertCanCompile(expression); - context.setVariable("var", StaticsHelper.class); - assertThat(expression.getValue(context).toString()).isEqualTo("sh"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression) parser.parseExpression("#var?.intValue()"); - context.setVariable("var", 4); - assertThat(expression.getValue(context).toString()).isEqualTo("4"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - assertCanCompile(expression); - context.setVariable("var", 4); - assertThat(expression.getValue(context).toString()).isEqualTo("4"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression) parser.parseExpression("#var?.booleanValue()"); - context.setVariable("var", false); - assertThat(expression.getValue(context).toString()).isEqualTo("false"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - assertCanCompile(expression); - context.setVariable("var", false); - assertThat(expression.getValue(context).toString()).isEqualTo("false"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression) parser.parseExpression("#var?.booleanValue()"); - context.setVariable("var", true); - assertThat(expression.getValue(context).toString()).isEqualTo("true"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - assertCanCompile(expression); - context.setVariable("var", true); - assertThat(expression.getValue(context).toString()).isEqualTo("true"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression) parser.parseExpression("#var?.longValue()"); - context.setVariable("var", 5L); - assertThat(expression.getValue(context).toString()).isEqualTo("5"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - assertCanCompile(expression); - context.setVariable("var", 5L); - assertThat(expression.getValue(context).toString()).isEqualTo("5"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression) parser.parseExpression("#var?.floatValue()"); - context.setVariable("var", 3f); - assertThat(expression.getValue(context).toString()).isEqualTo("3.0"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - assertCanCompile(expression); - context.setVariable("var", 3f); - assertThat(expression.getValue(context).toString()).isEqualTo("3.0"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression) parser.parseExpression("#var?.shortValue()"); - context.setVariable("var", (short)8); - assertThat(expression.getValue(context).toString()).isEqualTo("8"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - assertCanCompile(expression); - context.setVariable("var", (short)8); - assertThat(expression.getValue(context).toString()).isEqualTo("8"); - context.setVariable("var", null); - assertThat(expression.getValue(context)).isNull(); - } - - public static String concat(String a, String b) { - return a + b; - } - - public static String concat2(Object... args) { - return Arrays.stream(args) - .map(Objects::toString) - .collect(Collectors.joining()); - } - - public static String join(String...strings) { - StringBuilder buf = new StringBuilder(); - for (String string: strings) { - buf.append(string); - } - return buf.toString(); - } - - @Test - void compiledExpressionShouldWorkWhenUsingCustomFunctionWithVarargs() throws Exception { - StandardEvaluationContext context; - - // single string argument - expression = parser.parseExpression("#doFormat('hey %s', 'there')"); - context = new StandardEvaluationContext(); - context.registerFunction("doFormat", - DelegatingStringFormat.class.getDeclaredMethod("format", String.class, Object[].class)); - ((SpelExpression) expression).setEvaluationContext(context); - - assertThat(expression.getValue(String.class)).isEqualTo("hey there"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("hey there"); - - // single string argument from root array access - expression = parser.parseExpression("#doFormat([0], 'there')"); - context = new StandardEvaluationContext(new Object[] {"hey %s"}); - context.registerFunction("doFormat", - DelegatingStringFormat.class.getDeclaredMethod("format", String.class, Object[].class)); - ((SpelExpression) expression).setEvaluationContext(context); - - assertThat(expression.getValue(String.class)).isEqualTo("hey there"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("hey there"); - - // single string from variable - expression = parser.parseExpression("#doFormat([0], #arg)"); - context = new StandardEvaluationContext(new Object[] {"hey %s"}); - context.registerFunction("doFormat", - DelegatingStringFormat.class.getDeclaredMethod("format", String.class, Object[].class)); - context.setVariable("arg", "there"); - ((SpelExpression) expression).setEvaluationContext(context); - - assertThat(expression.getValue(String.class)).isEqualTo("hey there"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("hey there"); - - // string array argument - expression = parser.parseExpression("#doFormat('hey %s', #arg)"); - context = new StandardEvaluationContext(); - context.registerFunction("doFormat", - DelegatingStringFormat.class.getDeclaredMethod("format", String.class, Object[].class)); - context.setVariable("arg", new String[] { "there" }); - ((SpelExpression) expression).setEvaluationContext(context); - assertThat(expression.getValue(String.class)).isEqualTo("hey there"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("hey there"); - } - - @Test - void functionReference() throws Exception { - EvaluationContext ctx = new StandardEvaluationContext(); - Method m = getClass().getDeclaredMethod("concat", String.class, String.class); - ctx.setVariable("concat", m); - Method m2 = getClass().getDeclaredMethod("concat2", Object[].class); - ctx.setVariable("concat2", m2); - Method m3 = getClass().getDeclaredMethod("join", String[].class); - ctx.setVariable("join", m3); - - expression = parser.parseExpression("#concat('a','b')"); - assertThat(expression.getValue(ctx)).isEqualTo("ab"); - assertCanCompile(expression); - assertThat(expression.getValue(ctx)).isEqualTo("ab"); - - expression = parser.parseExpression("#concat(#concat('a','b'),'c').charAt(1)"); - assertThat(expression.getValue(ctx)).isEqualTo('b'); - assertCanCompile(expression); - assertThat(expression.getValue(ctx)).isEqualTo('b'); - - // varargs - expression = parser.parseExpression("#join(#stringArray)"); - ctx.setVariable("stringArray", new String[] { "a", "b", "c" }); - assertThat(expression.getValue(ctx)).isEqualTo("abc"); - assertCanCompile(expression); - assertThat(expression.getValue(ctx)).isEqualTo("abc"); - - // varargs with argument component type that is a subtype of the varargs component type. - expression = parser.parseExpression("#concat2(#stringArray)"); - ctx.setVariable("stringArray", new String[] { "a", "b", "c" }); - assertThat(expression.getValue(ctx)).isEqualTo("abc"); - assertCanCompile(expression); - assertThat(expression.getValue(ctx)).isEqualTo("abc"); - - expression = parser.parseExpression("#concat(#a,#b)"); - ctx.setVariable("a", "foo"); - ctx.setVariable("b", "bar"); - assertThat(expression.getValue(ctx)).isEqualTo("foobar"); - assertCanCompile(expression); - assertThat(expression.getValue(ctx)).isEqualTo("foobar"); - ctx.setVariable("b", "boo"); - assertThat(expression.getValue(ctx)).isEqualTo("fooboo"); - - m = Math.class.getDeclaredMethod("pow", double.class, double.class); - ctx.setVariable("kapow",m); - expression = parser.parseExpression("#kapow(2.0d,2.0d)"); - assertThat(expression.getValue(ctx).toString()).isEqualTo("4.0"); - assertCanCompile(expression); - assertThat(expression.getValue(ctx).toString()).isEqualTo("4.0"); - } - - @ParameterizedTest - @ValueSource(strings = {"voidMethod", "voidWrapperMethod"}) - void voidFunctionReference(String method) throws Exception { - assertVoidFunctionReferenceBehavior(method); - } - - private void assertVoidFunctionReferenceBehavior(String methodName) throws Exception { - Method method = getClass().getDeclaredMethod(methodName, String.class); - - EvaluationContext ctx = new StandardEvaluationContext(); - ctx.setVariable("voidMethod", method); - - expression = parser.parseExpression("#voidMethod('a')"); - - voidMethodInvokedWith = null; - expression.getValue(ctx); - assertThat(voidMethodInvokedWith).isEqualTo("a"); - assertCanCompile(expression); - - voidMethodInvokedWith = null; - expression.getValue(ctx); - assertThat(voidMethodInvokedWith).isEqualTo("a"); - assertCanCompile(expression); - - voidMethodInvokedWith = null; - expression.getValue(ctx); - assertThat(voidMethodInvokedWith).isEqualTo("a"); - assertCanCompile(expression); - - expression = parser.parseExpression("#voidMethod(#a)"); - ctx.setVariable("a", "foo"); - - voidMethodInvokedWith = null; - expression.getValue(ctx); - assertThat(voidMethodInvokedWith).isEqualTo("foo"); - assertCanCompile(expression); - - voidMethodInvokedWith = null; - expression.getValue(ctx); - assertThat(voidMethodInvokedWith).isEqualTo("foo"); - assertCanCompile(expression); - } - - private static String voidMethodInvokedWith; - - public static Void voidWrapperMethod(String str) { - voidMethodInvokedWith = str; - return null; - } - - public static void voidMethod(String str) { - voidMethodInvokedWith = str; - } - - @Test - void functionReferenceVisibility_SPR12359() throws Exception { - // Confirms visibility of what is being called. - StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {"1"}); - context.registerFunction("doCompare", SomeCompareMethod.class.getDeclaredMethod( - "compare", Object.class, Object.class)); - context.setVariable("arg", "2"); - // type nor method are public - expression = parser.parseExpression("#doCompare([0],#arg)"); - assertThat(expression.getValue(context, Integer.class).toString()).isEqualTo("-1"); - assertCannotCompile(expression); - - // type not public but method is - context = new StandardEvaluationContext(new Object[] {"1"}); - context.registerFunction("doCompare", SomeCompareMethod.class.getDeclaredMethod( - "compare2", Object.class, Object.class)); - context.setVariable("arg", "2"); - expression = parser.parseExpression("#doCompare([0],#arg)"); - assertThat(expression.getValue(context, Integer.class).toString()).isEqualTo("-1"); - assertCannotCompile(expression); - } - - @Test - void functionReferenceNonCompilableArguments_SPR12359() throws Exception { - StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {"1"}); - context.registerFunction("negate", SomeCompareMethod2.class.getDeclaredMethod( - "negate", int.class)); - context.setVariable("arg", "2"); - int[] ints = new int[] {1,2,3}; - context.setVariable("ints",ints); - - expression = parser.parseExpression("#negate(#ints.?[#this<2][0])"); - assertThat(expression.getValue(context, Integer.class).toString()).isEqualTo("-1"); - // Selection isn't compilable. - assertThat(((SpelExpression) expression).getAST().isCompilable()).isFalse(); - } - - @Test - void functionReferenceVarargs_SPR12359() throws Exception { - StandardEvaluationContext context = new StandardEvaluationContext(); - context.registerFunction("append", - SomeCompareMethod2.class.getDeclaredMethod("append", String[].class)); - context.registerFunction("append2", - SomeCompareMethod2.class.getDeclaredMethod("append2", Object[].class)); - context.registerFunction("append3", - SomeCompareMethod2.class.getDeclaredMethod("append3", String[].class)); - context.registerFunction("append4", - SomeCompareMethod2.class.getDeclaredMethod("append4", String.class, String[].class)); - context.registerFunction("appendChar", - SomeCompareMethod2.class.getDeclaredMethod("appendChar", char[].class)); - context.registerFunction("sum", - SomeCompareMethod2.class.getDeclaredMethod("sum", int[].class)); - context.registerFunction("sumDouble", - SomeCompareMethod2.class.getDeclaredMethod("sumDouble", double[].class)); - context.registerFunction("sumFloat", - SomeCompareMethod2.class.getDeclaredMethod("sumFloat", float[].class)); - context.setVariable("stringArray", new String[] {"x","y","z"}); - context.setVariable("intArray", new int[] {5,6,9}); - context.setVariable("doubleArray", new double[] {5.0d,6.0d,9.0d}); - context.setVariable("floatArray", new float[] {5.0f,6.0f,9.0f}); - - expression = parser.parseExpression("#append('a','b','c')"); - assertThat(expression.getValue(context).toString()).isEqualTo("abc"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context).toString()).isEqualTo("abc"); - - expression = parser.parseExpression("#append('a')"); - assertThat(expression.getValue(context).toString()).isEqualTo("a"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context).toString()).isEqualTo("a"); - - expression = parser.parseExpression("#append()"); - assertThat(expression.getValue(context).toString()).isEmpty(); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context).toString()).isEmpty(); - - expression = parser.parseExpression("#append(#stringArray)"); - assertThat(expression.getValue(context).toString()).isEqualTo("xyz"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context).toString()).isEqualTo("xyz"); - - // This is a methodreference invocation, to compare with functionreference - expression = parser.parseExpression("append(#stringArray)"); - assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("xyz"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("xyz"); - - expression = parser.parseExpression("#append2('a','b','c')"); - assertThat(expression.getValue(context).toString()).isEqualTo("abc"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context).toString()).isEqualTo("abc"); - - expression = parser.parseExpression("append2('a','b')"); - assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("ab"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("ab"); - - expression = parser.parseExpression("#append2('a','b')"); - assertThat(expression.getValue(context).toString()).isEqualTo("ab"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context).toString()).isEqualTo("ab"); - - expression = parser.parseExpression("#append2()"); - assertThat(expression.getValue(context).toString()).isEmpty(); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context).toString()).isEmpty(); - - expression = parser.parseExpression("#append3(#stringArray)"); - assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("xyz"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context, new SomeCompareMethod2()).toString()).isEqualTo("xyz"); - - expression = parser.parseExpression("#append2(#stringArray)"); - assertThat(expression.getValue(context)).hasToString("xyz"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).hasToString("xyz"); - - expression = parser.parseExpression("#sum(1,2,3)"); - assertThat(expression.getValue(context)).isEqualTo(6); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(6); - - expression = parser.parseExpression("#sum(2)"); - assertThat(expression.getValue(context)).isEqualTo(2); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(2); - - expression = parser.parseExpression("#sum()"); - assertThat(expression.getValue(context)).isEqualTo(0); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(0); - - expression = parser.parseExpression("#sum(#intArray)"); - assertThat(expression.getValue(context)).isEqualTo(20); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(20); - - expression = parser.parseExpression("#sumDouble(1.0d,2.0d,3.0d)"); - assertThat(expression.getValue(context)).isEqualTo(6); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(6); - - expression = parser.parseExpression("#sumDouble(2.0d)"); - assertThat(expression.getValue(context)).isEqualTo(2); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(2); - - expression = parser.parseExpression("#sumDouble()"); - assertThat(expression.getValue(context)).isEqualTo(0); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(0); - - expression = parser.parseExpression("#sumDouble(#doubleArray)"); - assertThat(expression.getValue(context)).isEqualTo(20); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(20); - - expression = parser.parseExpression("#sumFloat(1.0f,2.0f,3.0f)"); - assertThat(expression.getValue(context)).isEqualTo(6); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(6); - - expression = parser.parseExpression("#sumFloat(2.0f)"); - assertThat(expression.getValue(context)).isEqualTo(2); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(2); - - expression = parser.parseExpression("#sumFloat()"); - assertThat(expression.getValue(context)).isEqualTo(0); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(0); - - expression = parser.parseExpression("#sumFloat(#floatArray)"); - assertThat(expression.getValue(context)).isEqualTo(20); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo(20); - - expression = parser.parseExpression("#appendChar('abc'.charAt(0),'abc'.charAt(1))"); - assertThat(expression.getValue(context)).isEqualTo("ab"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo("ab"); - - expression = parser.parseExpression("#append4('a','b','c')"); - assertThat(expression.getValue(context).toString()).isEqualTo("a::bc"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context).toString()).isEqualTo("a::bc"); - - expression = parser.parseExpression("#append4('a','b')"); - assertThat(expression.getValue(context).toString()).isEqualTo("a::b"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context).toString()).isEqualTo("a::b"); - - expression = parser.parseExpression("#append4('a')"); - assertThat(expression.getValue(context).toString()).isEqualTo("a::"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context).toString()).isEqualTo("a::"); - - expression = parser.parseExpression("#append4('a',#stringArray)"); - assertThat(expression.getValue(context).toString()).isEqualTo("a::xyz"); - assertThat(((SpelExpression) expression).getAST().isCompilable()).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context).toString()).isEqualTo("a::xyz"); - } - - @Test - void functionReferenceVarargs() throws Exception { - EvaluationContext ctx = new StandardEvaluationContext(); - Method m = getClass().getDeclaredMethod("join", String[].class); - ctx.setVariable("join", m); - expression = parser.parseExpression("#join('a','b','c')"); - assertThat(expression.getValue(ctx)).isEqualTo("abc"); - assertCanCompile(expression); - assertThat(expression.getValue(ctx)).isEqualTo("abc"); - } - - @Test - void opLt() { - expression = parse("3.0d < 4.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("3446.0d < 1123.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("3 < 1"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("2 < 4"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("3.0f < 1.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("1.0f < 5.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("30L < 30L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("15L < 20L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - // Differing types of number, not yet supported - expression = parse("1 < 3.0d"); - assertCannotCompile(expression); - - expression = parse("T(Integer).valueOf(3) < 4"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("T(Integer).valueOf(3) < T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("5 < T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - } - - @Test - void opLe() { - expression = parse("3.0d <= 4.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("3446.0d <= 1123.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("3446.0d <= 3446.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("3 <= 1"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("2 <= 4"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("3 <= 3"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("3.0f <= 1.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("1.0f <= 5.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("2.0f <= 2.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("30L <= 30L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("15L <= 20L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - // Differing types of number, not yet supported - expression = parse("1 <= 3.0d"); - assertCannotCompile(expression); - - expression = parse("T(Integer).valueOf(3) <= 4"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("T(Integer).valueOf(3) <= T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("5 <= T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - } - - @Test - void opGt() { - expression = parse("3.0d > 4.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("3446.0d > 1123.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("3 > 1"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("2 > 4"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("3.0f > 1.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("1.0f > 5.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("30L > 30L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("15L > 20L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - // Differing types of number, not yet supported - expression = parse("1 > 3.0d"); - assertCannotCompile(expression); - - expression = parse("T(Integer).valueOf(3) > 4"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Integer).valueOf(3) > T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("5 > T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - } - - @Test - void opGe() { - expression = parse("3.0d >= 4.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("3446.0d >= 1123.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("3446.0d >= 3446.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("3 >= 1"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("2 >= 4"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("3 >= 3"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("3.0f >= 1.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("1.0f >= 5.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("3.0f >= 3.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("40L >= 30L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("15L >= 20L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("30L >= 30L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - // Differing types of number, not yet supported - expression = parse("1 >= 3.0d"); - assertCannotCompile(expression); - - expression = parse("T(Integer).valueOf(3) >= 4"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Integer).valueOf(3) >= T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("5 >= T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - } - - @Test - void opEq() { - String tvar = "35"; - expression = parse("#root == 35"); - assertThat((Boolean) expression.getValue(tvar)).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue(tvar)).isFalse(); - - expression = parse("35 == #root"); - expression.getValue(tvar); - assertThat((Boolean) expression.getValue(tvar)).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue(tvar)).isFalse(); - - TestClass7 tc7 = new TestClass7(); - TestClass7.reset(); - expression = parse("property == 'UK'"); - assertThat((Boolean) expression.getValue(tc7)).isTrue(); - TestClass7.property = null; - assertThat((Boolean) expression.getValue(tc7)).isFalse(); - assertCanCompile(expression); - TestClass7.reset(); - assertThat((Boolean) expression.getValue(tc7)).isTrue(); - TestClass7.property = "UK"; - assertThat((Boolean) expression.getValue(tc7)).isTrue(); - TestClass7.reset(); - TestClass7.property = null; - assertThat((Boolean) expression.getValue(tc7)).isFalse(); - expression = parse("property == null"); - assertThat((Boolean) expression.getValue(tc7)).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue(tc7)).isTrue(); - - expression = parse("3.0d == 4.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("3446.0d == 3446.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("3 == 1"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("3 == 3"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("3.0f == 1.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("2.0f == 2.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("30L == 30L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("15L == 20L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - // number types are not the same - expression = parse("1 == 3.0d"); - assertCannotCompile(expression); - - Double d = 3.0d; - expression = parse("#root==3.0d"); - assertThat((Boolean) expression.getValue(d)).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue(d)).isTrue(); - - Integer i = 3; - expression = parse("#root==3"); - assertThat((Boolean) expression.getValue(i)).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue(i)).isTrue(); - - Float f = 3.0f; - expression = parse("#root==3.0f"); - assertThat((Boolean) expression.getValue(f)).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue(f)).isTrue(); - - long l = 300L; - expression = parse("#root==300l"); - assertThat((Boolean) expression.getValue(l)).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue(l)).isTrue(); - - boolean b = true; - expression = parse("#root==true"); - assertThat((Boolean) expression.getValue(b)).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue(b)).isTrue(); - - expression = parse("T(Integer).valueOf(3) == 4"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Integer).valueOf(3) == T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("5 == T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Float).valueOf(3.0f) == 4.0f"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Float).valueOf(3.0f) == T(Float).valueOf(3.0f)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("5.0f == T(Float).valueOf(3.0f)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Long).valueOf(3L) == 4L"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Long).valueOf(3L) == T(Long).valueOf(3L)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("5L == T(Long).valueOf(3L)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Double).valueOf(3.0d) == 4.0d"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Double).valueOf(3.0d) == T(Double).valueOf(3.0d)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("5.0d == T(Double).valueOf(3.0d)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("false == true"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Boolean).valueOf('true') == T(Boolean).valueOf('true')"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("T(Boolean).valueOf('true') == true"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("false == T(Boolean).valueOf('false')"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - } - - @Test - void opNe() { - expression = parse("3.0d != 4.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("3446.0d != 3446.0d"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("3 != 1"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("3 != 3"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("3.0f != 1.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - expression = parse("2.0f != 2.0f"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("30L != 30L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - expression = parse("15L != 20L"); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - // not compatible number types - expression = parse("1 != 3.0d"); - assertCannotCompile(expression); - - expression = parse("T(Integer).valueOf(3) != 4"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("T(Integer).valueOf(3) != T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("5 != T(Integer).valueOf(3)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("T(Float).valueOf(3.0f) != 4.0f"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("T(Float).valueOf(3.0f) != T(Float).valueOf(3.0f)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("5.0f != T(Float).valueOf(3.0f)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("T(Long).valueOf(3L) != 4L"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("T(Long).valueOf(3L) != T(Long).valueOf(3L)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("5L != T(Long).valueOf(3L)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("T(Double).valueOf(3.0d) == 4.0d"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Double).valueOf(3.0d) == T(Double).valueOf(3.0d)"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("5.0d == T(Double).valueOf(3.0d)"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("false == true"); - assertThat((Boolean) expression.getValue()).isFalse(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isFalse(); - - expression = parse("T(Boolean).valueOf('true') == T(Boolean).valueOf('true')"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("T(Boolean).valueOf('true') == true"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - - expression = parse("false == T(Boolean).valueOf('false')"); - assertThat((Boolean) expression.getValue()).isTrue(); - assertCanCompile(expression); - assertThat((Boolean) expression.getValue()).isTrue(); - } - - @Test - void opNe_SPR14863() { - SpelParserConfiguration configuration = - new SpelParserConfiguration(SpelCompilerMode.MIXED, ClassLoader.getSystemClassLoader()); - SpelExpressionParser parser = new SpelExpressionParser(configuration); - Expression expression = parser.parseExpression("data['my-key'] != 'my-value'"); - - Map data = new HashMap<>(); - data.put("my-key", "my-value"); - StandardEvaluationContext context = new StandardEvaluationContext(new MyContext(data)); - assertThat(expression.getValue(context, Boolean.class)).isFalse(); - assertCanCompile(expression); - ((SpelExpression) expression).compileExpression(); - assertThat(expression.getValue(context, Boolean.class)).isFalse(); - - List ls = new ArrayList<>(); - ls.add("foo"); - context = new StandardEvaluationContext(ls); - expression = parse("get(0) != 'foo'"); - assertThat(expression.getValue(context, Boolean.class)).isFalse(); - assertCanCompile(expression); - assertThat(expression.getValue(context, Boolean.class)).isFalse(); - - ls.remove(0); - ls.add("goo"); - assertThat(expression.getValue(context, Boolean.class)).isTrue(); - } - - @Test - void opEq_SPR14863() { - // Exercise the comparator invocation code that runs in - // equalityCheck() (called from interpreted and compiled code) - expression = parser.parseExpression("#aa==#bb"); - StandardEvaluationContext sec = new StandardEvaluationContext(); - Apple aa = new Apple(1); - Apple bb = new Apple(2); - sec.setVariable("aa",aa); - sec.setVariable("bb",bb); - boolean b = expression.getValue(sec, Boolean.class); - // Verify what the expression caused aa to be compared to - assertThat(aa.gotComparedTo).isEqualTo(bb); - assertThat(b).isFalse(); - bb.setValue(1); - b = expression.getValue(sec, Boolean.class); - assertThat(aa.gotComparedTo).isEqualTo(bb); - assertThat(b).isTrue(); - - assertCanCompile(expression); - - // Similar test with compiled expression - aa = new Apple(99); - bb = new Apple(100); - sec.setVariable("aa",aa); - sec.setVariable("bb",bb); - b = expression.getValue(sec, Boolean.class); - assertThat(b).isFalse(); - assertThat(aa.gotComparedTo).isEqualTo(bb); - bb.setValue(99); - b = expression.getValue(sec, Boolean.class); - assertThat(b).isTrue(); - assertThat(aa.gotComparedTo).isEqualTo(bb); - - List ls = new ArrayList<>(); - ls.add("foo"); - StandardEvaluationContext context = new StandardEvaluationContext(ls); - expression = parse("get(0) == 'foo'"); - assertThat(expression.getValue(context, Boolean.class)).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context, Boolean.class)).isTrue(); - - ls.remove(0); - ls.add("goo"); - assertThat(expression.getValue(context, Boolean.class)).isFalse(); - } - - @Test - void opPlus() { - expression = parse("2+2"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(4); - - expression = parse("2L+2L"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(4L); - - expression = parse("2.0f+2.0f"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(4.0f); - - expression = parse("3.0d+4.0d"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(7.0d); - - expression = parse("+1"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1); - - expression = parse("+1L"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1L); - - expression = parse("+1.5f"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1.5f); - - expression = parse("+2.5d"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(2.5d); - - expression = parse("+T(Double).valueOf(2.5d)"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(2.5d); - - expression = parse("T(Integer).valueOf(2)+6"); - assertThat(expression.getValue()).isEqualTo(8); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(8); - - expression = parse("T(Integer).valueOf(1)+T(Integer).valueOf(3)"); - assertThat(expression.getValue()).isEqualTo(4); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(4); - - expression = parse("1+T(Integer).valueOf(3)"); - assertThat(expression.getValue()).isEqualTo(4); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(4); - - expression = parse("T(Float).valueOf(2.0f)+6"); - assertThat(expression.getValue()).isEqualTo(8.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(8.0f); - - expression = parse("T(Float).valueOf(2.0f)+T(Float).valueOf(3.0f)"); - assertThat(expression.getValue()).isEqualTo(5.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(5.0f); - - expression = parse("3L+T(Long).valueOf(4L)"); - assertThat(expression.getValue()).isEqualTo(7L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(7L); - - expression = parse("T(Long).valueOf(2L)+6"); - assertThat(expression.getValue()).isEqualTo(8L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(8L); - - expression = parse("T(Long).valueOf(2L)+T(Long).valueOf(3L)"); - assertThat(expression.getValue()).isEqualTo(5L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(5L); - - expression = parse("1L+T(Long).valueOf(2L)"); - assertThat(expression.getValue()).isEqualTo(3L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(3L); - } - - @Test - void opDivide_mixedNumberTypes() { - PayloadX p = new PayloadX(); - - // This is what you had to do before the changes in order for it to compile: - // expression = parse("(T(java.lang.Double).parseDouble(payload.valueI.toString()))/60D"); - - // right is a double - checkCalc(p,"payload.valueSB/60D",2d); - checkCalc(p,"payload.valueBB/60D",2d); - checkCalc(p,"payload.valueFB/60D",2d); - checkCalc(p,"payload.valueDB/60D",2d); - checkCalc(p,"payload.valueJB/60D",2d); - checkCalc(p,"payload.valueIB/60D",2d); - - checkCalc(p,"payload.valueS/60D",2d); - checkCalc(p,"payload.valueB/60D",2d); - checkCalc(p,"payload.valueF/60D",2d); - checkCalc(p,"payload.valueD/60D",2d); - checkCalc(p,"payload.valueJ/60D",2d); - checkCalc(p,"payload.valueI/60D",2d); - - checkCalc(p,"payload.valueSB/payload.valueDB60",2d); - checkCalc(p,"payload.valueBB/payload.valueDB60",2d); - checkCalc(p,"payload.valueFB/payload.valueDB60",2d); - checkCalc(p,"payload.valueDB/payload.valueDB60",2d); - checkCalc(p,"payload.valueJB/payload.valueDB60",2d); - checkCalc(p,"payload.valueIB/payload.valueDB60",2d); - - checkCalc(p,"payload.valueS/payload.valueDB60",2d); - checkCalc(p,"payload.valueB/payload.valueDB60",2d); - checkCalc(p,"payload.valueF/payload.valueDB60",2d); - checkCalc(p,"payload.valueD/payload.valueDB60",2d); - checkCalc(p,"payload.valueJ/payload.valueDB60",2d); - checkCalc(p,"payload.valueI/payload.valueDB60",2d); - - // right is a float - checkCalc(p,"payload.valueSB/60F",2F); - checkCalc(p,"payload.valueBB/60F",2F); - checkCalc(p,"payload.valueFB/60F",2f); - checkCalc(p,"payload.valueDB/60F",2d); - checkCalc(p,"payload.valueJB/60F",2F); - checkCalc(p,"payload.valueIB/60F",2F); - - checkCalc(p,"payload.valueS/60F",2F); - checkCalc(p,"payload.valueB/60F",2F); - checkCalc(p,"payload.valueF/60F",2f); - checkCalc(p,"payload.valueD/60F",2d); - checkCalc(p,"payload.valueJ/60F",2F); - checkCalc(p,"payload.valueI/60F",2F); - - checkCalc(p,"payload.valueSB/payload.valueFB60",2F); - checkCalc(p,"payload.valueBB/payload.valueFB60",2F); - checkCalc(p,"payload.valueFB/payload.valueFB60",2f); - checkCalc(p,"payload.valueDB/payload.valueFB60",2d); - checkCalc(p,"payload.valueJB/payload.valueFB60",2F); - checkCalc(p,"payload.valueIB/payload.valueFB60",2F); - - checkCalc(p,"payload.valueS/payload.valueFB60",2F); - checkCalc(p,"payload.valueB/payload.valueFB60",2F); - checkCalc(p,"payload.valueF/payload.valueFB60",2f); - checkCalc(p,"payload.valueD/payload.valueFB60",2d); - checkCalc(p,"payload.valueJ/payload.valueFB60",2F); - checkCalc(p,"payload.valueI/payload.valueFB60",2F); - - // right is a long - checkCalc(p,"payload.valueSB/60L",2L); - checkCalc(p,"payload.valueBB/60L",2L); - checkCalc(p,"payload.valueFB/60L",2f); - checkCalc(p,"payload.valueDB/60L",2d); - checkCalc(p,"payload.valueJB/60L",2L); - checkCalc(p,"payload.valueIB/60L",2L); - - checkCalc(p,"payload.valueS/60L",2L); - checkCalc(p,"payload.valueB/60L",2L); - checkCalc(p,"payload.valueF/60L",2f); - checkCalc(p,"payload.valueD/60L",2d); - checkCalc(p,"payload.valueJ/60L",2L); - checkCalc(p,"payload.valueI/60L",2L); - - checkCalc(p,"payload.valueSB/payload.valueJB60",2L); - checkCalc(p,"payload.valueBB/payload.valueJB60",2L); - checkCalc(p,"payload.valueFB/payload.valueJB60",2f); - checkCalc(p,"payload.valueDB/payload.valueJB60",2d); - checkCalc(p,"payload.valueJB/payload.valueJB60",2L); - checkCalc(p,"payload.valueIB/payload.valueJB60",2L); - - checkCalc(p,"payload.valueS/payload.valueJB60",2L); - checkCalc(p,"payload.valueB/payload.valueJB60",2L); - checkCalc(p,"payload.valueF/payload.valueJB60",2f); - checkCalc(p,"payload.valueD/payload.valueJB60",2d); - checkCalc(p,"payload.valueJ/payload.valueJB60",2L); - checkCalc(p,"payload.valueI/payload.valueJB60",2L); - - // right is an int - checkCalc(p,"payload.valueSB/60",2); - checkCalc(p,"payload.valueBB/60",2); - checkCalc(p,"payload.valueFB/60",2f); - checkCalc(p,"payload.valueDB/60",2d); - checkCalc(p,"payload.valueJB/60",2L); - checkCalc(p,"payload.valueIB/60",2); - - checkCalc(p,"payload.valueS/60",2); - checkCalc(p,"payload.valueB/60",2); - checkCalc(p,"payload.valueF/60",2f); - checkCalc(p,"payload.valueD/60",2d); - checkCalc(p,"payload.valueJ/60",2L); - checkCalc(p,"payload.valueI/60",2); - - checkCalc(p,"payload.valueSB/payload.valueIB60",2); - checkCalc(p,"payload.valueBB/payload.valueIB60",2); - checkCalc(p,"payload.valueFB/payload.valueIB60",2f); - checkCalc(p,"payload.valueDB/payload.valueIB60",2d); - checkCalc(p,"payload.valueJB/payload.valueIB60",2L); - checkCalc(p,"payload.valueIB/payload.valueIB60",2); - - checkCalc(p,"payload.valueS/payload.valueIB60",2); - checkCalc(p,"payload.valueB/payload.valueIB60",2); - checkCalc(p,"payload.valueF/payload.valueIB60",2f); - checkCalc(p,"payload.valueD/payload.valueIB60",2d); - checkCalc(p,"payload.valueJ/payload.valueIB60",2L); - checkCalc(p,"payload.valueI/payload.valueIB60",2); - - // right is a short - checkCalc(p,"payload.valueSB/payload.valueS",1); - checkCalc(p,"payload.valueBB/payload.valueS",1); - checkCalc(p,"payload.valueFB/payload.valueS",1f); - checkCalc(p,"payload.valueDB/payload.valueS",1d); - checkCalc(p,"payload.valueJB/payload.valueS",1L); - checkCalc(p,"payload.valueIB/payload.valueS",1); - - checkCalc(p,"payload.valueS/payload.valueS",1); - checkCalc(p,"payload.valueB/payload.valueS",1); - checkCalc(p,"payload.valueF/payload.valueS",1f); - checkCalc(p,"payload.valueD/payload.valueS",1d); - checkCalc(p,"payload.valueJ/payload.valueS",1L); - checkCalc(p,"payload.valueI/payload.valueS",1); - - checkCalc(p,"payload.valueSB/payload.valueSB",1); - checkCalc(p,"payload.valueBB/payload.valueSB",1); - checkCalc(p,"payload.valueFB/payload.valueSB",1f); - checkCalc(p,"payload.valueDB/payload.valueSB",1d); - checkCalc(p,"payload.valueJB/payload.valueSB",1L); - checkCalc(p,"payload.valueIB/payload.valueSB",1); - - checkCalc(p,"payload.valueS/payload.valueSB",1); - checkCalc(p,"payload.valueB/payload.valueSB",1); - checkCalc(p,"payload.valueF/payload.valueSB",1f); - checkCalc(p,"payload.valueD/payload.valueSB",1d); - checkCalc(p,"payload.valueJ/payload.valueSB",1L); - checkCalc(p,"payload.valueI/payload.valueSB",1); - - // right is a byte - checkCalc(p,"payload.valueSB/payload.valueB",1); - checkCalc(p,"payload.valueBB/payload.valueB",1); - checkCalc(p,"payload.valueFB/payload.valueB",1f); - checkCalc(p,"payload.valueDB/payload.valueB",1d); - checkCalc(p,"payload.valueJB/payload.valueB",1L); - checkCalc(p,"payload.valueIB/payload.valueB",1); - - checkCalc(p,"payload.valueS/payload.valueB",1); - checkCalc(p,"payload.valueB/payload.valueB",1); - checkCalc(p,"payload.valueF/payload.valueB",1f); - checkCalc(p,"payload.valueD/payload.valueB",1d); - checkCalc(p,"payload.valueJ/payload.valueB",1L); - checkCalc(p,"payload.valueI/payload.valueB",1); - - checkCalc(p,"payload.valueSB/payload.valueBB",1); - checkCalc(p,"payload.valueBB/payload.valueBB",1); - checkCalc(p,"payload.valueFB/payload.valueBB",1f); - checkCalc(p,"payload.valueDB/payload.valueBB",1d); - checkCalc(p,"payload.valueJB/payload.valueBB",1L); - checkCalc(p,"payload.valueIB/payload.valueBB",1); - - checkCalc(p,"payload.valueS/payload.valueBB",1); - checkCalc(p,"payload.valueB/payload.valueBB",1); - checkCalc(p,"payload.valueF/payload.valueBB",1f); - checkCalc(p,"payload.valueD/payload.valueBB",1d); - checkCalc(p,"payload.valueJ/payload.valueBB",1L); - checkCalc(p,"payload.valueI/payload.valueBB",1); - } - - @Test - void opPlus_mixedNumberTypes() { - PayloadX p = new PayloadX(); - - // This is what you had to do before the changes in order for it to compile: - // expression = parse("(T(java.lang.Double).parseDouble(payload.valueI.toString()))/60D"); - - // right is a double - checkCalc(p,"payload.valueSB+60D",180d); - checkCalc(p,"payload.valueBB+60D",180d); - checkCalc(p,"payload.valueFB+60D",180d); - checkCalc(p,"payload.valueDB+60D",180d); - checkCalc(p,"payload.valueJB+60D",180d); - checkCalc(p,"payload.valueIB+60D",180d); - - checkCalc(p,"payload.valueS+60D",180d); - checkCalc(p,"payload.valueB+60D",180d); - checkCalc(p,"payload.valueF+60D",180d); - checkCalc(p,"payload.valueD+60D",180d); - checkCalc(p,"payload.valueJ+60D",180d); - checkCalc(p,"payload.valueI+60D",180d); - - checkCalc(p,"payload.valueSB+payload.valueDB60",180d); - checkCalc(p,"payload.valueBB+payload.valueDB60",180d); - checkCalc(p,"payload.valueFB+payload.valueDB60",180d); - checkCalc(p,"payload.valueDB+payload.valueDB60",180d); - checkCalc(p,"payload.valueJB+payload.valueDB60",180d); - checkCalc(p,"payload.valueIB+payload.valueDB60",180d); - - checkCalc(p,"payload.valueS+payload.valueDB60",180d); - checkCalc(p,"payload.valueB+payload.valueDB60",180d); - checkCalc(p,"payload.valueF+payload.valueDB60",180d); - checkCalc(p,"payload.valueD+payload.valueDB60",180d); - checkCalc(p,"payload.valueJ+payload.valueDB60",180d); - checkCalc(p,"payload.valueI+payload.valueDB60",180d); - - // right is a float - checkCalc(p,"payload.valueSB+60F",180F); - checkCalc(p,"payload.valueBB+60F",180F); - checkCalc(p,"payload.valueFB+60F",180f); - checkCalc(p,"payload.valueDB+60F",180d); - checkCalc(p,"payload.valueJB+60F",180F); - checkCalc(p,"payload.valueIB+60F",180F); - - checkCalc(p,"payload.valueS+60F",180F); - checkCalc(p,"payload.valueB+60F",180F); - checkCalc(p,"payload.valueF+60F",180f); - checkCalc(p,"payload.valueD+60F",180d); - checkCalc(p,"payload.valueJ+60F",180F); - checkCalc(p,"payload.valueI+60F",180F); - - checkCalc(p,"payload.valueSB+payload.valueFB60",180F); - checkCalc(p,"payload.valueBB+payload.valueFB60",180F); - checkCalc(p,"payload.valueFB+payload.valueFB60",180f); - checkCalc(p,"payload.valueDB+payload.valueFB60",180d); - checkCalc(p,"payload.valueJB+payload.valueFB60",180F); - checkCalc(p,"payload.valueIB+payload.valueFB60",180F); - - checkCalc(p,"payload.valueS+payload.valueFB60",180F); - checkCalc(p,"payload.valueB+payload.valueFB60",180F); - checkCalc(p,"payload.valueF+payload.valueFB60",180f); - checkCalc(p,"payload.valueD+payload.valueFB60",180d); - checkCalc(p,"payload.valueJ+payload.valueFB60",180F); - checkCalc(p,"payload.valueI+payload.valueFB60",180F); - - // right is a long - checkCalc(p,"payload.valueSB+60L",180L); - checkCalc(p,"payload.valueBB+60L",180L); - checkCalc(p,"payload.valueFB+60L",180f); - checkCalc(p,"payload.valueDB+60L",180d); - checkCalc(p,"payload.valueJB+60L",180L); - checkCalc(p,"payload.valueIB+60L",180L); - - checkCalc(p,"payload.valueS+60L",180L); - checkCalc(p,"payload.valueB+60L",180L); - checkCalc(p,"payload.valueF+60L",180f); - checkCalc(p,"payload.valueD+60L",180d); - checkCalc(p,"payload.valueJ+60L",180L); - checkCalc(p,"payload.valueI+60L",180L); - - checkCalc(p,"payload.valueSB+payload.valueJB60",180L); - checkCalc(p,"payload.valueBB+payload.valueJB60",180L); - checkCalc(p,"payload.valueFB+payload.valueJB60",180f); - checkCalc(p,"payload.valueDB+payload.valueJB60",180d); - checkCalc(p,"payload.valueJB+payload.valueJB60",180L); - checkCalc(p,"payload.valueIB+payload.valueJB60",180L); - - checkCalc(p,"payload.valueS+payload.valueJB60",180L); - checkCalc(p,"payload.valueB+payload.valueJB60",180L); - checkCalc(p,"payload.valueF+payload.valueJB60",180f); - checkCalc(p,"payload.valueD+payload.valueJB60",180d); - checkCalc(p,"payload.valueJ+payload.valueJB60",180L); - checkCalc(p,"payload.valueI+payload.valueJB60",180L); - - // right is an int - checkCalc(p,"payload.valueSB+60",180); - checkCalc(p,"payload.valueBB+60",180); - checkCalc(p,"payload.valueFB+60",180f); - checkCalc(p,"payload.valueDB+60",180d); - checkCalc(p,"payload.valueJB+60",180L); - checkCalc(p,"payload.valueIB+60",180); - - checkCalc(p,"payload.valueS+60",180); - checkCalc(p,"payload.valueB+60",180); - checkCalc(p,"payload.valueF+60",180f); - checkCalc(p,"payload.valueD+60",180d); - checkCalc(p,"payload.valueJ+60",180L); - checkCalc(p,"payload.valueI+60",180); - - checkCalc(p,"payload.valueSB+payload.valueIB60",180); - checkCalc(p,"payload.valueBB+payload.valueIB60",180); - checkCalc(p,"payload.valueFB+payload.valueIB60",180f); - checkCalc(p,"payload.valueDB+payload.valueIB60",180d); - checkCalc(p,"payload.valueJB+payload.valueIB60",180L); - checkCalc(p,"payload.valueIB+payload.valueIB60",180); - - checkCalc(p,"payload.valueS+payload.valueIB60",180); - checkCalc(p,"payload.valueB+payload.valueIB60",180); - checkCalc(p,"payload.valueF+payload.valueIB60",180f); - checkCalc(p,"payload.valueD+payload.valueIB60",180d); - checkCalc(p,"payload.valueJ+payload.valueIB60",180L); - checkCalc(p,"payload.valueI+payload.valueIB60",180); - - // right is a short - checkCalc(p,"payload.valueSB+payload.valueS",240); - checkCalc(p,"payload.valueBB+payload.valueS",240); - checkCalc(p,"payload.valueFB+payload.valueS",240f); - checkCalc(p,"payload.valueDB+payload.valueS",240d); - checkCalc(p,"payload.valueJB+payload.valueS",240L); - checkCalc(p,"payload.valueIB+payload.valueS",240); - - checkCalc(p,"payload.valueS+payload.valueS",240); - checkCalc(p,"payload.valueB+payload.valueS",240); - checkCalc(p,"payload.valueF+payload.valueS",240f); - checkCalc(p,"payload.valueD+payload.valueS",240d); - checkCalc(p,"payload.valueJ+payload.valueS",240L); - checkCalc(p,"payload.valueI+payload.valueS",240); - - checkCalc(p,"payload.valueSB+payload.valueSB",240); - checkCalc(p,"payload.valueBB+payload.valueSB",240); - checkCalc(p,"payload.valueFB+payload.valueSB",240f); - checkCalc(p,"payload.valueDB+payload.valueSB",240d); - checkCalc(p,"payload.valueJB+payload.valueSB",240L); - checkCalc(p,"payload.valueIB+payload.valueSB",240); - - checkCalc(p,"payload.valueS+payload.valueSB",240); - checkCalc(p,"payload.valueB+payload.valueSB",240); - checkCalc(p,"payload.valueF+payload.valueSB",240f); - checkCalc(p,"payload.valueD+payload.valueSB",240d); - checkCalc(p,"payload.valueJ+payload.valueSB",240L); - checkCalc(p,"payload.valueI+payload.valueSB",240); - - // right is a byte - checkCalc(p,"payload.valueSB+payload.valueB",240); - checkCalc(p,"payload.valueBB+payload.valueB",240); - checkCalc(p,"payload.valueFB+payload.valueB",240f); - checkCalc(p,"payload.valueDB+payload.valueB",240d); - checkCalc(p,"payload.valueJB+payload.valueB",240L); - checkCalc(p,"payload.valueIB+payload.valueB",240); - - checkCalc(p,"payload.valueS+payload.valueB",240); - checkCalc(p,"payload.valueB+payload.valueB",240); - checkCalc(p,"payload.valueF+payload.valueB",240f); - checkCalc(p,"payload.valueD+payload.valueB",240d); - checkCalc(p,"payload.valueJ+payload.valueB",240L); - checkCalc(p,"payload.valueI+payload.valueB",240); - - checkCalc(p,"payload.valueSB+payload.valueBB",240); - checkCalc(p,"payload.valueBB+payload.valueBB",240); - checkCalc(p,"payload.valueFB+payload.valueBB",240f); - checkCalc(p,"payload.valueDB+payload.valueBB",240d); - checkCalc(p,"payload.valueJB+payload.valueBB",240L); - checkCalc(p,"payload.valueIB+payload.valueBB",240); - - checkCalc(p,"payload.valueS+payload.valueBB",240); - checkCalc(p,"payload.valueB+payload.valueBB",240); - checkCalc(p,"payload.valueF+payload.valueBB",240f); - checkCalc(p,"payload.valueD+payload.valueBB",240d); - checkCalc(p,"payload.valueJ+payload.valueBB",240L); - checkCalc(p,"payload.valueI+payload.valueBB",240); - } - - private void checkCalc(PayloadX p, String expression, int expectedResult) { - Expression expr = parse(expression); - assertThat(expr.getValue(p)).isEqualTo(expectedResult); - assertCanCompile(expr); - assertThat(expr.getValue(p)).isEqualTo(expectedResult); - } - - private void checkCalc(PayloadX p, String expression, float expectedResult) { - Expression expr = parse(expression); - assertThat(expr.getValue(p)).isEqualTo(expectedResult); - assertCanCompile(expr); - assertThat(expr.getValue(p)).isEqualTo(expectedResult); - } - - private void checkCalc(PayloadX p, String expression, long expectedResult) { - Expression expr = parse(expression); - assertThat(expr.getValue(p)).isEqualTo(expectedResult); - assertCanCompile(expr); - assertThat(expr.getValue(p)).isEqualTo(expectedResult); - } - - private void checkCalc(PayloadX p, String expression, double expectedResult) { - Expression expr = parse(expression); - assertThat(expr.getValue(p)).isEqualTo(expectedResult); - assertCanCompile(expr); - assertThat(expr.getValue(p)).isEqualTo(expectedResult); - } - - @Test - void opPlusString() { - expression = parse("'hello' + 'world'"); - assertThat(expression.getValue()).isEqualTo("helloworld"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo("helloworld"); - - // Method with string return - expression = parse("'hello' + getWorld()"); - assertThat(expression.getValue(new Greeter())).isEqualTo("helloworld"); - assertCanCompile(expression); - assertThat(expression.getValue(new Greeter())).isEqualTo("helloworld"); - - // Method with string return - expression = parse("getWorld() + 'hello'"); - assertThat(expression.getValue(new Greeter())).isEqualTo("worldhello"); - assertCanCompile(expression); - assertThat(expression.getValue(new Greeter())).isEqualTo("worldhello"); - - // Three strings, optimal bytecode would only use one StringBuilder - expression = parse("'hello' + getWorld() + ' spring'"); - assertThat(expression.getValue(new Greeter())).isEqualTo("helloworld spring"); - assertCanCompile(expression); - assertThat(expression.getValue(new Greeter())).isEqualTo("helloworld spring"); - - // Three strings, optimal bytecode would only use one StringBuilder - expression = parse("'hello' + 3 + ' spring'"); - assertThat(expression.getValue(new Greeter())).isEqualTo("hello3 spring"); - assertCannotCompile(expression); - - expression = parse("object + 'a'"); - assertThat(expression.getValue(new Greeter())).isEqualTo("objecta"); - assertCanCompile(expression); - assertThat(expression.getValue(new Greeter())).isEqualTo("objecta"); - - expression = parse("'a'+object"); - assertThat(expression.getValue(new Greeter())).isEqualTo("aobject"); - assertCanCompile(expression); - assertThat(expression.getValue(new Greeter())).isEqualTo("aobject"); - - expression = parse("'a'+object+'a'"); - assertThat(expression.getValue(new Greeter())).isEqualTo("aobjecta"); - assertCanCompile(expression); - assertThat(expression.getValue(new Greeter())).isEqualTo("aobjecta"); - - expression = parse("object+'a'+object"); - assertThat(expression.getValue(new Greeter())).isEqualTo("objectaobject"); - assertCanCompile(expression); - assertThat(expression.getValue(new Greeter())).isEqualTo("objectaobject"); - - expression = parse("object+object"); - assertThat(expression.getValue(new Greeter())).isEqualTo("objectobject"); - assertCanCompile(expression); - assertThat(expression.getValue(new Greeter())).isEqualTo("objectobject"); - } - - @Test - void opMinus() { - expression = parse("2-2"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(0); - - expression = parse("4L-2L"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(2L); - - expression = parse("4.0f-2.0f"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(2.0f); - - expression = parse("3.0d-4.0d"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(-1.0d); - - expression = parse("-1"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(-1); - - expression = parse("-1L"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(-1L); - - expression = parse("-1.5f"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(-1.5f); - - expression = parse("-2.5d"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(-2.5d); - - expression = parse("T(Integer).valueOf(2)-6"); - assertThat(expression.getValue()).isEqualTo(-4); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(-4); - - expression = parse("T(Integer).valueOf(1)-T(Integer).valueOf(3)"); - assertThat(expression.getValue()).isEqualTo(-2); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(-2); - - expression = parse("4-T(Integer).valueOf(3)"); - assertThat(expression.getValue()).isEqualTo(1); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1); - - expression = parse("T(Float).valueOf(2.0f)-6"); - assertThat(expression.getValue()).isEqualTo(-4.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(-4.0f); - - expression = parse("T(Float).valueOf(8.0f)-T(Float).valueOf(3.0f)"); - assertThat(expression.getValue()).isEqualTo(5.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(5.0f); - - expression = parse("11L-T(Long).valueOf(4L)"); - assertThat(expression.getValue()).isEqualTo(7L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(7L); - - expression = parse("T(Long).valueOf(9L)-6"); - assertThat(expression.getValue()).isEqualTo(3L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(3L); - - expression = parse("T(Long).valueOf(4L)-T(Long).valueOf(3L)"); - assertThat(expression.getValue()).isEqualTo(1L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1L); - - expression = parse("8L-T(Long).valueOf(2L)"); - assertThat(expression.getValue()).isEqualTo(6L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(6L); - } - - @Test - void opMinus_mixedNumberTypes() { - PayloadX p = new PayloadX(); - - // This is what you had to do before the changes in order for it to compile: - // expression = parse("(T(java.lang.Double).parseDouble(payload.valueI.toString()))/60D"); - - // right is a double - checkCalc(p,"payload.valueSB-60D",60d); - checkCalc(p,"payload.valueBB-60D",60d); - checkCalc(p,"payload.valueFB-60D",60d); - checkCalc(p,"payload.valueDB-60D",60d); - checkCalc(p,"payload.valueJB-60D",60d); - checkCalc(p,"payload.valueIB-60D",60d); - - checkCalc(p,"payload.valueS-60D",60d); - checkCalc(p,"payload.valueB-60D",60d); - checkCalc(p,"payload.valueF-60D",60d); - checkCalc(p,"payload.valueD-60D",60d); - checkCalc(p,"payload.valueJ-60D",60d); - checkCalc(p,"payload.valueI-60D",60d); - - checkCalc(p,"payload.valueSB-payload.valueDB60",60d); - checkCalc(p,"payload.valueBB-payload.valueDB60",60d); - checkCalc(p,"payload.valueFB-payload.valueDB60",60d); - checkCalc(p,"payload.valueDB-payload.valueDB60",60d); - checkCalc(p,"payload.valueJB-payload.valueDB60",60d); - checkCalc(p,"payload.valueIB-payload.valueDB60",60d); - - checkCalc(p,"payload.valueS-payload.valueDB60",60d); - checkCalc(p,"payload.valueB-payload.valueDB60",60d); - checkCalc(p,"payload.valueF-payload.valueDB60",60d); - checkCalc(p,"payload.valueD-payload.valueDB60",60d); - checkCalc(p,"payload.valueJ-payload.valueDB60",60d); - checkCalc(p,"payload.valueI-payload.valueDB60",60d); - - // right is a float - checkCalc(p,"payload.valueSB-60F",60F); - checkCalc(p,"payload.valueBB-60F",60F); - checkCalc(p,"payload.valueFB-60F",60f); - checkCalc(p,"payload.valueDB-60F",60d); - checkCalc(p,"payload.valueJB-60F",60F); - checkCalc(p,"payload.valueIB-60F",60F); - - checkCalc(p,"payload.valueS-60F",60F); - checkCalc(p,"payload.valueB-60F",60F); - checkCalc(p,"payload.valueF-60F",60f); - checkCalc(p,"payload.valueD-60F",60d); - checkCalc(p,"payload.valueJ-60F",60F); - checkCalc(p,"payload.valueI-60F",60F); - - checkCalc(p,"payload.valueSB-payload.valueFB60",60F); - checkCalc(p,"payload.valueBB-payload.valueFB60",60F); - checkCalc(p,"payload.valueFB-payload.valueFB60",60f); - checkCalc(p,"payload.valueDB-payload.valueFB60",60d); - checkCalc(p,"payload.valueJB-payload.valueFB60",60F); - checkCalc(p,"payload.valueIB-payload.valueFB60",60F); - - checkCalc(p,"payload.valueS-payload.valueFB60",60F); - checkCalc(p,"payload.valueB-payload.valueFB60",60F); - checkCalc(p,"payload.valueF-payload.valueFB60",60f); - checkCalc(p,"payload.valueD-payload.valueFB60",60d); - checkCalc(p,"payload.valueJ-payload.valueFB60",60F); - checkCalc(p,"payload.valueI-payload.valueFB60",60F); - - // right is a long - checkCalc(p,"payload.valueSB-60L",60L); - checkCalc(p,"payload.valueBB-60L",60L); - checkCalc(p,"payload.valueFB-60L",60f); - checkCalc(p,"payload.valueDB-60L",60d); - checkCalc(p,"payload.valueJB-60L",60L); - checkCalc(p,"payload.valueIB-60L",60L); - - checkCalc(p,"payload.valueS-60L",60L); - checkCalc(p,"payload.valueB-60L",60L); - checkCalc(p,"payload.valueF-60L",60f); - checkCalc(p,"payload.valueD-60L",60d); - checkCalc(p,"payload.valueJ-60L",60L); - checkCalc(p,"payload.valueI-60L",60L); - - checkCalc(p,"payload.valueSB-payload.valueJB60",60L); - checkCalc(p,"payload.valueBB-payload.valueJB60",60L); - checkCalc(p,"payload.valueFB-payload.valueJB60",60f); - checkCalc(p,"payload.valueDB-payload.valueJB60",60d); - checkCalc(p,"payload.valueJB-payload.valueJB60",60L); - checkCalc(p,"payload.valueIB-payload.valueJB60",60L); - - checkCalc(p,"payload.valueS-payload.valueJB60",60L); - checkCalc(p,"payload.valueB-payload.valueJB60",60L); - checkCalc(p,"payload.valueF-payload.valueJB60",60f); - checkCalc(p,"payload.valueD-payload.valueJB60",60d); - checkCalc(p,"payload.valueJ-payload.valueJB60",60L); - checkCalc(p,"payload.valueI-payload.valueJB60",60L); - - // right is an int - checkCalc(p,"payload.valueSB-60",60); - checkCalc(p,"payload.valueBB-60",60); - checkCalc(p,"payload.valueFB-60",60f); - checkCalc(p,"payload.valueDB-60",60d); - checkCalc(p,"payload.valueJB-60",60L); - checkCalc(p,"payload.valueIB-60",60); - - checkCalc(p,"payload.valueS-60",60); - checkCalc(p,"payload.valueB-60",60); - checkCalc(p,"payload.valueF-60",60f); - checkCalc(p,"payload.valueD-60",60d); - checkCalc(p,"payload.valueJ-60",60L); - checkCalc(p,"payload.valueI-60",60); - - checkCalc(p,"payload.valueSB-payload.valueIB60",60); - checkCalc(p,"payload.valueBB-payload.valueIB60",60); - checkCalc(p,"payload.valueFB-payload.valueIB60",60f); - checkCalc(p,"payload.valueDB-payload.valueIB60",60d); - checkCalc(p,"payload.valueJB-payload.valueIB60",60L); - checkCalc(p,"payload.valueIB-payload.valueIB60",60); - - checkCalc(p,"payload.valueS-payload.valueIB60",60); - checkCalc(p,"payload.valueB-payload.valueIB60",60); - checkCalc(p,"payload.valueF-payload.valueIB60",60f); - checkCalc(p,"payload.valueD-payload.valueIB60",60d); - checkCalc(p,"payload.valueJ-payload.valueIB60",60L); - checkCalc(p,"payload.valueI-payload.valueIB60",60); - - // right is a short - checkCalc(p,"payload.valueSB-payload.valueS20",100); - checkCalc(p,"payload.valueBB-payload.valueS20",100); - checkCalc(p,"payload.valueFB-payload.valueS20",100f); - checkCalc(p,"payload.valueDB-payload.valueS20",100d); - checkCalc(p,"payload.valueJB-payload.valueS20",100L); - checkCalc(p,"payload.valueIB-payload.valueS20",100); - - checkCalc(p,"payload.valueS-payload.valueS20",100); - checkCalc(p,"payload.valueB-payload.valueS20",100); - checkCalc(p,"payload.valueF-payload.valueS20",100f); - checkCalc(p,"payload.valueD-payload.valueS20",100d); - checkCalc(p,"payload.valueJ-payload.valueS20",100L); - checkCalc(p,"payload.valueI-payload.valueS20",100); - - checkCalc(p,"payload.valueSB-payload.valueSB20",100); - checkCalc(p,"payload.valueBB-payload.valueSB20",100); - checkCalc(p,"payload.valueFB-payload.valueSB20",100f); - checkCalc(p,"payload.valueDB-payload.valueSB20",100d); - checkCalc(p,"payload.valueJB-payload.valueSB20",100L); - checkCalc(p,"payload.valueIB-payload.valueSB20",100); - - checkCalc(p,"payload.valueS-payload.valueSB20",100); - checkCalc(p,"payload.valueB-payload.valueSB20",100); - checkCalc(p,"payload.valueF-payload.valueSB20",100f); - checkCalc(p,"payload.valueD-payload.valueSB20",100d); - checkCalc(p,"payload.valueJ-payload.valueSB20",100L); - checkCalc(p,"payload.valueI-payload.valueSB20",100); - - // right is a byte - checkCalc(p,"payload.valueSB-payload.valueB20",100); - checkCalc(p,"payload.valueBB-payload.valueB20",100); - checkCalc(p,"payload.valueFB-payload.valueB20",100f); - checkCalc(p,"payload.valueDB-payload.valueB20",100d); - checkCalc(p,"payload.valueJB-payload.valueB20",100L); - checkCalc(p,"payload.valueIB-payload.valueB20",100); - - checkCalc(p,"payload.valueS-payload.valueB20",100); - checkCalc(p,"payload.valueB-payload.valueB20",100); - checkCalc(p,"payload.valueF-payload.valueB20",100f); - checkCalc(p,"payload.valueD-payload.valueB20",100d); - checkCalc(p,"payload.valueJ-payload.valueB20",100L); - checkCalc(p,"payload.valueI-payload.valueB20",100); - - checkCalc(p,"payload.valueSB-payload.valueBB20",100); - checkCalc(p,"payload.valueBB-payload.valueBB20",100); - checkCalc(p,"payload.valueFB-payload.valueBB20",100f); - checkCalc(p,"payload.valueDB-payload.valueBB20",100d); - checkCalc(p,"payload.valueJB-payload.valueBB20",100L); - checkCalc(p,"payload.valueIB-payload.valueBB20",100); - - checkCalc(p,"payload.valueS-payload.valueBB20",100); - checkCalc(p,"payload.valueB-payload.valueBB20",100); - checkCalc(p,"payload.valueF-payload.valueBB20",100f); - checkCalc(p,"payload.valueD-payload.valueBB20",100d); - checkCalc(p,"payload.valueJ-payload.valueBB20",100L); - checkCalc(p,"payload.valueI-payload.valueBB20",100); - } - - @Test - void opMultiply_mixedNumberTypes() { - PayloadX p = new PayloadX(); - - // This is what you had to do before the changes in order for it to compile: - // expression = parse("(T(java.lang.Double).parseDouble(payload.valueI.toString()))/60D"); - - // right is a double - checkCalc(p,"payload.valueSB*60D",7200d); - checkCalc(p,"payload.valueBB*60D",7200d); - checkCalc(p,"payload.valueFB*60D",7200d); - checkCalc(p,"payload.valueDB*60D",7200d); - checkCalc(p,"payload.valueJB*60D",7200d); - checkCalc(p,"payload.valueIB*60D",7200d); - - checkCalc(p,"payload.valueS*60D",7200d); - checkCalc(p,"payload.valueB*60D",7200d); - checkCalc(p,"payload.valueF*60D",7200d); - checkCalc(p,"payload.valueD*60D",7200d); - checkCalc(p,"payload.valueJ*60D",7200d); - checkCalc(p,"payload.valueI*60D",7200d); - - checkCalc(p,"payload.valueSB*payload.valueDB60",7200d); - checkCalc(p,"payload.valueBB*payload.valueDB60",7200d); - checkCalc(p,"payload.valueFB*payload.valueDB60",7200d); - checkCalc(p,"payload.valueDB*payload.valueDB60",7200d); - checkCalc(p,"payload.valueJB*payload.valueDB60",7200d); - checkCalc(p,"payload.valueIB*payload.valueDB60",7200d); - - checkCalc(p,"payload.valueS*payload.valueDB60",7200d); - checkCalc(p,"payload.valueB*payload.valueDB60",7200d); - checkCalc(p,"payload.valueF*payload.valueDB60",7200d); - checkCalc(p,"payload.valueD*payload.valueDB60",7200d); - checkCalc(p,"payload.valueJ*payload.valueDB60",7200d); - checkCalc(p,"payload.valueI*payload.valueDB60",7200d); - - // right is a float - checkCalc(p,"payload.valueSB*60F",7200F); - checkCalc(p,"payload.valueBB*60F",7200F); - checkCalc(p,"payload.valueFB*60F",7200f); - checkCalc(p,"payload.valueDB*60F",7200d); - checkCalc(p,"payload.valueJB*60F",7200F); - checkCalc(p,"payload.valueIB*60F",7200F); - - checkCalc(p,"payload.valueS*60F",7200F); - checkCalc(p,"payload.valueB*60F",7200F); - checkCalc(p,"payload.valueF*60F",7200f); - checkCalc(p,"payload.valueD*60F",7200d); - checkCalc(p,"payload.valueJ*60F",7200F); - checkCalc(p,"payload.valueI*60F",7200F); - - checkCalc(p,"payload.valueSB*payload.valueFB60",7200F); - checkCalc(p,"payload.valueBB*payload.valueFB60",7200F); - checkCalc(p,"payload.valueFB*payload.valueFB60",7200f); - checkCalc(p,"payload.valueDB*payload.valueFB60",7200d); - checkCalc(p,"payload.valueJB*payload.valueFB60",7200F); - checkCalc(p,"payload.valueIB*payload.valueFB60",7200F); - - checkCalc(p,"payload.valueS*payload.valueFB60",7200F); - checkCalc(p,"payload.valueB*payload.valueFB60",7200F); - checkCalc(p,"payload.valueF*payload.valueFB60",7200f); - checkCalc(p,"payload.valueD*payload.valueFB60",7200d); - checkCalc(p,"payload.valueJ*payload.valueFB60",7200F); - checkCalc(p,"payload.valueI*payload.valueFB60",7200F); - - // right is a long - checkCalc(p,"payload.valueSB*60L",7200L); - checkCalc(p,"payload.valueBB*60L",7200L); - checkCalc(p,"payload.valueFB*60L",7200f); - checkCalc(p,"payload.valueDB*60L",7200d); - checkCalc(p,"payload.valueJB*60L",7200L); - checkCalc(p,"payload.valueIB*60L",7200L); - - checkCalc(p,"payload.valueS*60L",7200L); - checkCalc(p,"payload.valueB*60L",7200L); - checkCalc(p,"payload.valueF*60L",7200f); - checkCalc(p,"payload.valueD*60L",7200d); - checkCalc(p,"payload.valueJ*60L",7200L); - checkCalc(p,"payload.valueI*60L",7200L); - - checkCalc(p,"payload.valueSB*payload.valueJB60",7200L); - checkCalc(p,"payload.valueBB*payload.valueJB60",7200L); - checkCalc(p,"payload.valueFB*payload.valueJB60",7200f); - checkCalc(p,"payload.valueDB*payload.valueJB60",7200d); - checkCalc(p,"payload.valueJB*payload.valueJB60",7200L); - checkCalc(p,"payload.valueIB*payload.valueJB60",7200L); - - checkCalc(p,"payload.valueS*payload.valueJB60",7200L); - checkCalc(p,"payload.valueB*payload.valueJB60",7200L); - checkCalc(p,"payload.valueF*payload.valueJB60",7200f); - checkCalc(p,"payload.valueD*payload.valueJB60",7200d); - checkCalc(p,"payload.valueJ*payload.valueJB60",7200L); - checkCalc(p,"payload.valueI*payload.valueJB60",7200L); - - // right is an int - checkCalc(p,"payload.valueSB*60",7200); - checkCalc(p,"payload.valueBB*60",7200); - checkCalc(p,"payload.valueFB*60",7200f); - checkCalc(p,"payload.valueDB*60",7200d); - checkCalc(p,"payload.valueJB*60",7200L); - checkCalc(p,"payload.valueIB*60",7200); - - checkCalc(p,"payload.valueS*60",7200); - checkCalc(p,"payload.valueB*60",7200); - checkCalc(p,"payload.valueF*60",7200f); - checkCalc(p,"payload.valueD*60",7200d); - checkCalc(p,"payload.valueJ*60",7200L); - checkCalc(p,"payload.valueI*60",7200); - - checkCalc(p,"payload.valueSB*payload.valueIB60",7200); - checkCalc(p,"payload.valueBB*payload.valueIB60",7200); - checkCalc(p,"payload.valueFB*payload.valueIB60",7200f); - checkCalc(p,"payload.valueDB*payload.valueIB60",7200d); - checkCalc(p,"payload.valueJB*payload.valueIB60",7200L); - checkCalc(p,"payload.valueIB*payload.valueIB60",7200); - - checkCalc(p,"payload.valueS*payload.valueIB60",7200); - checkCalc(p,"payload.valueB*payload.valueIB60",7200); - checkCalc(p,"payload.valueF*payload.valueIB60",7200f); - checkCalc(p,"payload.valueD*payload.valueIB60",7200d); - checkCalc(p,"payload.valueJ*payload.valueIB60",7200L); - checkCalc(p,"payload.valueI*payload.valueIB60",7200); - - // right is a short - checkCalc(p,"payload.valueSB*payload.valueS20",2400); - checkCalc(p,"payload.valueBB*payload.valueS20",2400); - checkCalc(p,"payload.valueFB*payload.valueS20",2400f); - checkCalc(p,"payload.valueDB*payload.valueS20",2400d); - checkCalc(p,"payload.valueJB*payload.valueS20",2400L); - checkCalc(p,"payload.valueIB*payload.valueS20",2400); - - checkCalc(p,"payload.valueS*payload.valueS20",2400); - checkCalc(p,"payload.valueB*payload.valueS20",2400); - checkCalc(p,"payload.valueF*payload.valueS20",2400f); - checkCalc(p,"payload.valueD*payload.valueS20",2400d); - checkCalc(p,"payload.valueJ*payload.valueS20",2400L); - checkCalc(p,"payload.valueI*payload.valueS20",2400); - - checkCalc(p,"payload.valueSB*payload.valueSB20",2400); - checkCalc(p,"payload.valueBB*payload.valueSB20",2400); - checkCalc(p,"payload.valueFB*payload.valueSB20",2400f); - checkCalc(p,"payload.valueDB*payload.valueSB20",2400d); - checkCalc(p,"payload.valueJB*payload.valueSB20",2400L); - checkCalc(p,"payload.valueIB*payload.valueSB20",2400); - - checkCalc(p,"payload.valueS*payload.valueSB20",2400); - checkCalc(p,"payload.valueB*payload.valueSB20",2400); - checkCalc(p,"payload.valueF*payload.valueSB20",2400f); - checkCalc(p,"payload.valueD*payload.valueSB20",2400d); - checkCalc(p,"payload.valueJ*payload.valueSB20",2400L); - checkCalc(p,"payload.valueI*payload.valueSB20",2400); - - // right is a byte - checkCalc(p,"payload.valueSB*payload.valueB20",2400); - checkCalc(p,"payload.valueBB*payload.valueB20",2400); - checkCalc(p,"payload.valueFB*payload.valueB20",2400f); - checkCalc(p,"payload.valueDB*payload.valueB20",2400d); - checkCalc(p,"payload.valueJB*payload.valueB20",2400L); - checkCalc(p,"payload.valueIB*payload.valueB20",2400); - - checkCalc(p,"payload.valueS*payload.valueB20",2400); - checkCalc(p,"payload.valueB*payload.valueB20",2400); - checkCalc(p,"payload.valueF*payload.valueB20",2400f); - checkCalc(p,"payload.valueD*payload.valueB20",2400d); - checkCalc(p,"payload.valueJ*payload.valueB20",2400L); - checkCalc(p,"payload.valueI*payload.valueB20",2400); - - checkCalc(p,"payload.valueSB*payload.valueBB20",2400); - checkCalc(p,"payload.valueBB*payload.valueBB20",2400); - checkCalc(p,"payload.valueFB*payload.valueBB20",2400f); - checkCalc(p,"payload.valueDB*payload.valueBB20",2400d); - checkCalc(p,"payload.valueJB*payload.valueBB20",2400L); - checkCalc(p,"payload.valueIB*payload.valueBB20",2400); - - checkCalc(p,"payload.valueS*payload.valueBB20",2400); - checkCalc(p,"payload.valueB*payload.valueBB20",2400); - checkCalc(p,"payload.valueF*payload.valueBB20",2400f); - checkCalc(p,"payload.valueD*payload.valueBB20",2400d); - checkCalc(p,"payload.valueJ*payload.valueBB20",2400L); - checkCalc(p,"payload.valueI*payload.valueBB20",2400); - } - - @Test - void opModulus_mixedNumberTypes() { - PayloadX p = new PayloadX(); - - // This is what you had to do before the changes in order for it to compile: - // expression = parse("(T(java.lang.Double).parseDouble(payload.valueI.toString()))/60D"); - - // right is a double - checkCalc(p,"payload.valueSB%58D",4d); - checkCalc(p,"payload.valueBB%58D",4d); - checkCalc(p,"payload.valueFB%58D",4d); - checkCalc(p,"payload.valueDB%58D",4d); - checkCalc(p,"payload.valueJB%58D",4d); - checkCalc(p,"payload.valueIB%58D",4d); - - checkCalc(p,"payload.valueS%58D",4d); - checkCalc(p,"payload.valueB%58D",4d); - checkCalc(p,"payload.valueF%58D",4d); - checkCalc(p,"payload.valueD%58D",4d); - checkCalc(p,"payload.valueJ%58D",4d); - checkCalc(p,"payload.valueI%58D",4d); - - checkCalc(p,"payload.valueSB%payload.valueDB58",4d); - checkCalc(p,"payload.valueBB%payload.valueDB58",4d); - checkCalc(p,"payload.valueFB%payload.valueDB58",4d); - checkCalc(p,"payload.valueDB%payload.valueDB58",4d); - checkCalc(p,"payload.valueJB%payload.valueDB58",4d); - checkCalc(p,"payload.valueIB%payload.valueDB58",4d); - - checkCalc(p,"payload.valueS%payload.valueDB58",4d); - checkCalc(p,"payload.valueB%payload.valueDB58",4d); - checkCalc(p,"payload.valueF%payload.valueDB58",4d); - checkCalc(p,"payload.valueD%payload.valueDB58",4d); - checkCalc(p,"payload.valueJ%payload.valueDB58",4d); - checkCalc(p,"payload.valueI%payload.valueDB58",4d); - - // right is a float - checkCalc(p,"payload.valueSB%58F",4F); - checkCalc(p,"payload.valueBB%58F",4F); - checkCalc(p,"payload.valueFB%58F",4f); - checkCalc(p,"payload.valueDB%58F",4d); - checkCalc(p,"payload.valueJB%58F",4F); - checkCalc(p,"payload.valueIB%58F",4F); - - checkCalc(p,"payload.valueS%58F",4F); - checkCalc(p,"payload.valueB%58F",4F); - checkCalc(p,"payload.valueF%58F",4f); - checkCalc(p,"payload.valueD%58F",4d); - checkCalc(p,"payload.valueJ%58F",4F); - checkCalc(p,"payload.valueI%58F",4F); - - checkCalc(p,"payload.valueSB%payload.valueFB58",4F); - checkCalc(p,"payload.valueBB%payload.valueFB58",4F); - checkCalc(p,"payload.valueFB%payload.valueFB58",4f); - checkCalc(p,"payload.valueDB%payload.valueFB58",4d); - checkCalc(p,"payload.valueJB%payload.valueFB58",4F); - checkCalc(p,"payload.valueIB%payload.valueFB58",4F); - - checkCalc(p,"payload.valueS%payload.valueFB58",4F); - checkCalc(p,"payload.valueB%payload.valueFB58",4F); - checkCalc(p,"payload.valueF%payload.valueFB58",4f); - checkCalc(p,"payload.valueD%payload.valueFB58",4d); - checkCalc(p,"payload.valueJ%payload.valueFB58",4F); - checkCalc(p,"payload.valueI%payload.valueFB58",4F); - - // right is a long - checkCalc(p,"payload.valueSB%58L",4L); - checkCalc(p,"payload.valueBB%58L",4L); - checkCalc(p,"payload.valueFB%58L",4f); - checkCalc(p,"payload.valueDB%58L",4d); - checkCalc(p,"payload.valueJB%58L",4L); - checkCalc(p,"payload.valueIB%58L",4L); - - checkCalc(p,"payload.valueS%58L",4L); - checkCalc(p,"payload.valueB%58L",4L); - checkCalc(p,"payload.valueF%58L",4f); - checkCalc(p,"payload.valueD%58L",4d); - checkCalc(p,"payload.valueJ%58L",4L); - checkCalc(p,"payload.valueI%58L",4L); - - checkCalc(p,"payload.valueSB%payload.valueJB58",4L); - checkCalc(p,"payload.valueBB%payload.valueJB58",4L); - checkCalc(p,"payload.valueFB%payload.valueJB58",4f); - checkCalc(p,"payload.valueDB%payload.valueJB58",4d); - checkCalc(p,"payload.valueJB%payload.valueJB58",4L); - checkCalc(p,"payload.valueIB%payload.valueJB58",4L); - - checkCalc(p,"payload.valueS%payload.valueJB58",4L); - checkCalc(p,"payload.valueB%payload.valueJB58",4L); - checkCalc(p,"payload.valueF%payload.valueJB58",4f); - checkCalc(p,"payload.valueD%payload.valueJB58",4d); - checkCalc(p,"payload.valueJ%payload.valueJB58",4L); - checkCalc(p,"payload.valueI%payload.valueJB58",4L); - - // right is an int - checkCalc(p,"payload.valueSB%58",4); - checkCalc(p,"payload.valueBB%58",4); - checkCalc(p,"payload.valueFB%58",4f); - checkCalc(p,"payload.valueDB%58",4d); - checkCalc(p,"payload.valueJB%58",4L); - checkCalc(p,"payload.valueIB%58",4); - - checkCalc(p,"payload.valueS%58",4); - checkCalc(p,"payload.valueB%58",4); - checkCalc(p,"payload.valueF%58",4f); - checkCalc(p,"payload.valueD%58",4d); - checkCalc(p,"payload.valueJ%58",4L); - checkCalc(p,"payload.valueI%58",4); - - checkCalc(p,"payload.valueSB%payload.valueIB58",4); - checkCalc(p,"payload.valueBB%payload.valueIB58",4); - checkCalc(p,"payload.valueFB%payload.valueIB58",4f); - checkCalc(p,"payload.valueDB%payload.valueIB58",4d); - checkCalc(p,"payload.valueJB%payload.valueIB58",4L); - checkCalc(p,"payload.valueIB%payload.valueIB58",4); - - checkCalc(p,"payload.valueS%payload.valueIB58",4); - checkCalc(p,"payload.valueB%payload.valueIB58",4); - checkCalc(p,"payload.valueF%payload.valueIB58",4f); - checkCalc(p,"payload.valueD%payload.valueIB58",4d); - checkCalc(p,"payload.valueJ%payload.valueIB58",4L); - checkCalc(p,"payload.valueI%payload.valueIB58",4); - - // right is a short - checkCalc(p,"payload.valueSB%payload.valueS18",12); - checkCalc(p,"payload.valueBB%payload.valueS18",12); - checkCalc(p,"payload.valueFB%payload.valueS18",12f); - checkCalc(p,"payload.valueDB%payload.valueS18",12d); - checkCalc(p,"payload.valueJB%payload.valueS18",12L); - checkCalc(p,"payload.valueIB%payload.valueS18",12); - - checkCalc(p,"payload.valueS%payload.valueS18",12); - checkCalc(p,"payload.valueB%payload.valueS18",12); - checkCalc(p,"payload.valueF%payload.valueS18",12f); - checkCalc(p,"payload.valueD%payload.valueS18",12d); - checkCalc(p,"payload.valueJ%payload.valueS18",12L); - checkCalc(p,"payload.valueI%payload.valueS18",12); - - checkCalc(p,"payload.valueSB%payload.valueSB18",12); - checkCalc(p,"payload.valueBB%payload.valueSB18",12); - checkCalc(p,"payload.valueFB%payload.valueSB18",12f); - checkCalc(p,"payload.valueDB%payload.valueSB18",12d); - checkCalc(p,"payload.valueJB%payload.valueSB18",12L); - checkCalc(p,"payload.valueIB%payload.valueSB18",12); - - checkCalc(p,"payload.valueS%payload.valueSB18",12); - checkCalc(p,"payload.valueB%payload.valueSB18",12); - checkCalc(p,"payload.valueF%payload.valueSB18",12f); - checkCalc(p,"payload.valueD%payload.valueSB18",12d); - checkCalc(p,"payload.valueJ%payload.valueSB18",12L); - checkCalc(p,"payload.valueI%payload.valueSB18",12); - - // right is a byte - checkCalc(p,"payload.valueSB%payload.valueB18",12); - checkCalc(p,"payload.valueBB%payload.valueB18",12); - checkCalc(p,"payload.valueFB%payload.valueB18",12f); - checkCalc(p,"payload.valueDB%payload.valueB18",12d); - checkCalc(p,"payload.valueJB%payload.valueB18",12L); - checkCalc(p,"payload.valueIB%payload.valueB18",12); - - checkCalc(p,"payload.valueS%payload.valueB18",12); - checkCalc(p,"payload.valueB%payload.valueB18",12); - checkCalc(p,"payload.valueF%payload.valueB18",12f); - checkCalc(p,"payload.valueD%payload.valueB18",12d); - checkCalc(p,"payload.valueJ%payload.valueB18",12L); - checkCalc(p,"payload.valueI%payload.valueB18",12); - - checkCalc(p,"payload.valueSB%payload.valueBB18",12); - checkCalc(p,"payload.valueBB%payload.valueBB18",12); - checkCalc(p,"payload.valueFB%payload.valueBB18",12f); - checkCalc(p,"payload.valueDB%payload.valueBB18",12d); - checkCalc(p,"payload.valueJB%payload.valueBB18",12L); - checkCalc(p,"payload.valueIB%payload.valueBB18",12); - - checkCalc(p,"payload.valueS%payload.valueBB18",12); - checkCalc(p,"payload.valueB%payload.valueBB18",12); - checkCalc(p,"payload.valueF%payload.valueBB18",12f); - checkCalc(p,"payload.valueD%payload.valueBB18",12d); - checkCalc(p,"payload.valueJ%payload.valueBB18",12L); - checkCalc(p,"payload.valueI%payload.valueBB18",12); - } - - @Test - void opMultiply() { - expression = parse("2*2"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(4); - - expression = parse("2L*2L"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(4L); - - expression = parse("2.0f*2.0f"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(4.0f); - - expression = parse("3.0d*4.0d"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(12.0d); - - expression = parse("T(Float).valueOf(2.0f)*6"); - assertThat(expression.getValue()).isEqualTo(12.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(12.0f); - - expression = parse("T(Float).valueOf(8.0f)*T(Float).valueOf(3.0f)"); - assertThat(expression.getValue()).isEqualTo(24.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(24.0f); - - expression = parse("11L*T(Long).valueOf(4L)"); - assertThat(expression.getValue()).isEqualTo(44L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(44L); - - expression = parse("T(Long).valueOf(9L)*6"); - assertThat(expression.getValue()).isEqualTo(54L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(54L); - - expression = parse("T(Long).valueOf(4L)*T(Long).valueOf(3L)"); - assertThat(expression.getValue()).isEqualTo(12L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(12L); - - expression = parse("8L*T(Long).valueOf(2L)"); - assertThat(expression.getValue()).isEqualTo(16L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(16L); - - expression = parse("T(Float).valueOf(8.0f)*-T(Float).valueOf(3.0f)"); - assertThat(expression.getValue()).isEqualTo(-24.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(-24.0f); - } - - @Test - void opDivide() { - expression = parse("2/2"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1); - - expression = parse("2L/2L"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1L); - - expression = parse("2.0f/2.0f"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1.0f); - - expression = parse("3.0d/4.0d"); - expression.getValue(); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(0.75d); - - expression = parse("T(Float).valueOf(6.0f)/2"); - assertThat(expression.getValue()).isEqualTo(3.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(3.0f); - - expression = parse("T(Float).valueOf(8.0f)/T(Float).valueOf(2.0f)"); - assertThat(expression.getValue()).isEqualTo(4.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(4.0f); - - expression = parse("12L/T(Long).valueOf(4L)"); - assertThat(expression.getValue()).isEqualTo(3L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(3L); - - expression = parse("T(Long).valueOf(44L)/11"); - assertThat(expression.getValue()).isEqualTo(4L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(4L); - - expression = parse("T(Long).valueOf(4L)/T(Long).valueOf(2L)"); - assertThat(expression.getValue()).isEqualTo(2L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(2L); - - expression = parse("8L/T(Long).valueOf(2L)"); - assertThat(expression.getValue()).isEqualTo(4L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(4L); - - expression = parse("T(Float).valueOf(8.0f)/-T(Float).valueOf(4.0f)"); - assertThat(expression.getValue()).isEqualTo(-2.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(-2.0f); - } - - @Test - void opModulus_12041() { - expression = parse("2%2"); - assertThat(expression.getValue()).isEqualTo(0); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(0); - - expression = parse("payload%2==0"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4), boolean.class)).isTrue(); - assertThat(expression.getValue(new GenericMessageTestHelper<>(5), boolean.class)).isFalse(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4), boolean.class)).isTrue(); - assertThat(expression.getValue(new GenericMessageTestHelper<>(5), boolean.class)).isFalse(); - - expression = parse("8%3"); - assertThat(expression.getValue()).isEqualTo(2); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(2); - - expression = parse("17L%5L"); - assertThat(expression.getValue()).isEqualTo(2L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(2L); - - expression = parse("3.0f%2.0f"); - assertThat(expression.getValue()).isEqualTo(1.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1.0f); - - expression = parse("3.0d%4.0d"); - assertThat(expression.getValue()).isEqualTo(3.0d); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(3.0d); - - expression = parse("T(Float).valueOf(6.0f)%2"); - assertThat(expression.getValue()).isEqualTo(0.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(0.0f); - - expression = parse("T(Float).valueOf(6.0f)%4"); - assertThat(expression.getValue()).isEqualTo(2.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(2.0f); - - expression = parse("T(Float).valueOf(8.0f)%T(Float).valueOf(3.0f)"); - assertThat(expression.getValue()).isEqualTo(2.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(2.0f); - - expression = parse("13L%T(Long).valueOf(4L)"); - assertThat(expression.getValue()).isEqualTo(1L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1L); - - expression = parse("T(Long).valueOf(44L)%12"); - assertThat(expression.getValue()).isEqualTo(8L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(8L); - - expression = parse("T(Long).valueOf(9L)%T(Long).valueOf(2L)"); - assertThat(expression.getValue()).isEqualTo(1L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1L); - - expression = parse("7L%T(Long).valueOf(2L)"); - assertThat(expression.getValue()).isEqualTo(1L); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1L); - - expression = parse("T(Float).valueOf(9.0f)%-T(Float).valueOf(4.0f)"); - assertThat(expression.getValue()).isEqualTo(1.0f); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo(1.0f); - } - - @Test - void compilationOfBasicNullSafeMethodReference() { - SpelExpressionParser parser = new SpelExpressionParser( - new SpelParserConfiguration(SpelCompilerMode.OFF, getClass().getClassLoader())); - SpelExpression expression = parser.parseRaw("#it?.equals(3)"); - StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {1}); - context.setVariable("it", 3); - expression.setEvaluationContext(context); - assertThat(expression.getValue(Boolean.class)).isTrue(); - context.setVariable("it", null); - assertThat(expression.getValue(Boolean.class)).isNull(); - - assertCanCompile(expression); - - context.setVariable("it", 3); - assertThat(expression.getValue(Boolean.class)).isTrue(); - context.setVariable("it", null); - assertThat(expression.getValue(Boolean.class)).isNull(); - } - - @Test - void failsWhenSettingContextForExpression_SPR12326() { - SpelExpressionParser parser = new SpelExpressionParser( - new SpelParserConfiguration(SpelCompilerMode.OFF, getClass().getClassLoader())); - Person3 person = new Person3("foo", 1); - SpelExpression expression = parser.parseRaw("#it?.age?.equals([0])"); - StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {1}); - context.setVariable("it", person); - expression.setEvaluationContext(context); - assertThat(expression.getValue(Boolean.class)).isTrue(); - // This will trigger compilation (second usage) - assertThat(expression.getValue(Boolean.class)).isTrue(); - context.setVariable("it", null); - assertThat(expression.getValue(Boolean.class)).isNull(); - - assertCanCompile(expression); - - context.setVariable("it", person); - assertThat(expression.getValue(Boolean.class)).isTrue(); - context.setVariable("it", null); - assertThat(expression.getValue(Boolean.class)).isNull(); - } - - /** - * Test variants of using T(...) and static/non-static method/property/field references. - */ - @Test - void constructorReference_SPR13781() { - // Static field access on a T() referenced type - expression = parser.parseExpression("T(java.util.Locale).ENGLISH"); - assertThat(expression.getValue().toString()).isEqualTo("en"); - assertCanCompile(expression); - assertThat(expression.getValue().toString()).isEqualTo("en"); - - // The actual expression from the bug report. It fails if the ENGLISH reference fails - // to pop the type reference for Locale off the stack (if it isn't popped then - // toLowerCase() will be called with a Locale parameter). In this situation the - // code generation for ENGLISH should notice there is something on the stack that - // is not required and pop it off. - expression = parser.parseExpression("#userId.toString().toLowerCase(T(java.util.Locale).ENGLISH)"); - StandardEvaluationContext context = new StandardEvaluationContext(); - context.setVariable("userId", "RoDnEy"); - assertThat(expression.getValue(context)).isEqualTo("rodney"); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo("rodney"); - - // Property access on a class object - expression = parser.parseExpression("T(String).name"); - assertThat(expression.getValue()).isEqualTo("java.lang.String"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo("java.lang.String"); - - // Now the type reference isn't on the stack, and needs loading - context = new StandardEvaluationContext(String.class); - expression = parser.parseExpression("name"); - assertThat(expression.getValue(context)).isEqualTo("java.lang.String"); - assertCanCompile(expression); - assertThat(expression.getValue(context)).isEqualTo("java.lang.String"); - - expression = parser.parseExpression("T(String).getName()"); - assertThat(expression.getValue()).isEqualTo("java.lang.String"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo("java.lang.String"); - - // These tests below verify that the chain of static accesses (either method/property or field) - // leave the right thing on top of the stack for processing by any outer consuming code. - // Here the consuming code is the String.valueOf() function. If the wrong thing were on - // the stack (for example if the compiled code for static methods wasn't popping the - // previous thing off the stack) the valueOf() would operate on the wrong value. - - String shclass = StaticsHelper.class.getName(); - // Basic chain: property access then method access - expression = parser.parseExpression("T(String).valueOf(T(String).name.valueOf(1))"); - assertThat(expression.getValue()).isEqualTo("1"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo("1"); - - // chain of statics ending with static method - expression = parser.parseExpression("T(String).valueOf(T(" + shclass + ").methoda().methoda().methodb())"); - assertThat(expression.getValue()).isEqualTo("mb"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo("mb"); - - // chain of statics ending with static field - expression = parser.parseExpression("T(String).valueOf(T(" + shclass + ").fielda.fielda.fieldb)"); - assertThat(expression.getValue()).isEqualTo("fb"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo("fb"); - - // chain of statics ending with static property access - expression = parser.parseExpression("T(String).valueOf(T(" + shclass + ").propertya.propertya.propertyb)"); - assertThat(expression.getValue()).isEqualTo("pb"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo("pb"); - - // variety chain - expression = parser.parseExpression("T(String).valueOf(T(" + shclass + ").fielda.methoda().propertya.fieldb)"); - assertThat(expression.getValue()).isEqualTo("fb"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo("fb"); - - expression = parser.parseExpression("T(String).valueOf(fielda.fieldb)"); - assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("fb"); - assertCanCompile(expression); - assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("fb"); - - expression = parser.parseExpression("T(String).valueOf(propertya.propertyb)"); - assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("pb"); - assertCanCompile(expression); - assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("pb"); - - expression = parser.parseExpression("T(String).valueOf(methoda().methodb())"); - assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("mb"); - assertCanCompile(expression); - assertThat(expression.getValue(StaticsHelper.sh)).isEqualTo("mb"); - } - - @Test - void constructorReference_SPR12326() { - String type = getClass().getName(); - String prefix = "new " + type + ".Obj"; - - expression = parser.parseExpression(prefix + "([0])"); - assertThat(((Obj) expression.getValue(new Object[]{"test"})).param1).isEqualTo("test"); - assertCanCompile(expression); - assertThat(((Obj) expression.getValue(new Object[]{"test"})).param1).isEqualTo("test"); - - expression = parser.parseExpression(prefix + "2('foo','bar').output"); - assertThat(expression.getValue(String.class)).isEqualTo("foobar"); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("foobar"); - - expression = parser.parseExpression(prefix + "2('foo').output"); - assertThat(expression.getValue(String.class)).isEqualTo("foo"); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("foo"); - - expression = parser.parseExpression(prefix + "2().output"); - assertThat(expression.getValue(String.class)).isEmpty(); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEmpty(); - - expression = parser.parseExpression(prefix + "3(1,2,3).output"); - assertThat(expression.getValue(String.class)).isEqualTo("123"); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("123"); - - expression = parser.parseExpression(prefix + "3(1).output"); - assertThat(expression.getValue(String.class)).isEqualTo("1"); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("1"); - - expression = parser.parseExpression(prefix + "3().output"); - assertThat(expression.getValue(String.class)).isEmpty(); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEmpty(); - - expression = parser.parseExpression(prefix + "3('abc',5.0f,1,2,3).output"); - assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:123"); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:123"); - - expression = parser.parseExpression(prefix + "3('abc',5.0f,1).output"); - assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:1"); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:1"); - - expression = parser.parseExpression(prefix + "3('abc',5.0f).output"); - assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:"); - assertCanCompile(expression); - assertThat(expression.getValue(String.class)).isEqualTo("abc:5.0:"); - - expression = parser.parseExpression(prefix + "4(#root).output"); - assertThat(expression.getValue(new int[] {1,2,3}, String.class)).isEqualTo("123"); - assertCanCompile(expression); - assertThat(expression.getValue(new int[] {1,2,3}, String.class)).isEqualTo("123"); - } - - @Test - void methodReferenceMissingCastAndRootObjectAccessing_SPR12326() { - // Need boxing code on the 1 so that toString() can be called - expression = parser.parseExpression("1.toString()"); - assertThat(expression.getValue()).isEqualTo("1"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo("1"); - - expression = parser.parseExpression("#it?.age.equals([0])"); - Person person = new Person(1); - StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {person.getAge()}); - context.setVariable("it", person); - assertThat(expression.getValue(context, Boolean.class)).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(context, Boolean.class)).isTrue(); - - // Variant of above more like what was in the bug report: - SpelExpressionParser parser = new SpelExpressionParser( - new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, getClass().getClassLoader())); - - SpelExpression ex = parser.parseRaw("#it?.age.equals([0])"); - context = new StandardEvaluationContext(new Object[] {person.getAge()}); - context.setVariable("it", person); - assertThat(ex.getValue(context, Boolean.class)).isTrue(); - - PersonInOtherPackage person2 = new PersonInOtherPackage(1); - ex = parser.parseRaw("#it?.age.equals([0])"); - context = new StandardEvaluationContext(new Object[] {person2.getAge()}); - context.setVariable("it", person2); - assertThat(ex.getValue(context, Boolean.class)).isTrue(); - - ex = parser.parseRaw("#it?.age.equals([0])"); - context = new StandardEvaluationContext(new Object[] {person2.getAge()}); - context.setVariable("it", person2); - assertThat((Boolean) ex.getValue(context)).isTrue(); - } - - @Test - void constructorReference() { - // simple constructor - expression = parser.parseExpression("new String('123')"); - assertThat(expression.getValue()).isEqualTo("123"); - assertCanCompile(expression); - assertThat(expression.getValue()).isEqualTo("123"); - - String testclass8 = TestClass8.class.getName(); - Object result; - - // multi arg constructor that includes primitives - expression = parser.parseExpression("new " + testclass8 + "(42,'123',4.0d,true)"); - result = expression.getValue(); - assertThat(result).isExactlyInstanceOf(TestClass8.class); - assertCanCompile(expression); - result = expression.getValue(); - assertThat(result).isExactlyInstanceOf(TestClass8.class); - TestClass8 tc8 = (TestClass8) result; - assertThat(tc8.i).isEqualTo(42); - assertThat(tc8.s).isEqualTo("123"); - assertThat(tc8.d).isCloseTo(4.0d, within(0.5d)); - - assertThat(tc8.z).isTrue(); - - // no-arg constructor - expression = parser.parseExpression("new " + testclass8 + "()"); - result = expression.getValue(); - assertThat(result).isExactlyInstanceOf(TestClass8.class); - assertCanCompile(expression); - result = expression.getValue(); - assertThat(result).isExactlyInstanceOf(TestClass8.class); - - // pass primitive to reference type constructor - expression = parser.parseExpression("new " + testclass8 + "(42)"); - result = expression.getValue(); - assertThat(result).isExactlyInstanceOf(TestClass8.class); - assertCanCompile(expression); - result = expression.getValue(); - assertThat(result).isExactlyInstanceOf(TestClass8.class); - tc8 = (TestClass8) result; - assertThat(tc8.i).isEqualTo(42); - - // varargs - expression = parser.parseExpression("new " + testclass8 + "(#root)"); - Object[] objectArray = { "a", "b", "c" }; - result = expression.getValue(objectArray); - assertThat(result).isExactlyInstanceOf(TestClass8.class); - assertCanCompile(expression); - result = expression.getValue(objectArray); - assertThat(result).isExactlyInstanceOf(TestClass8.class); - tc8 = (TestClass8) result; - assertThat(tc8.args).containsExactly("a", "b", "c"); - - // varargs with argument component type that is a subtype of the varargs component type. - expression = parser.parseExpression("new " + testclass8 + "(#root)"); - String[] stringArray = { "a", "b", "c" }; - result = expression.getValue(stringArray); - assertThat(result).isExactlyInstanceOf(TestClass8.class); - assertCanCompile(expression); - result = expression.getValue(stringArray); - assertThat(result).isExactlyInstanceOf(TestClass8.class); - tc8 = (TestClass8) result; - assertThat(tc8.args).containsExactly("a", "b", "c"); - - // private class, can't compile it - String testclass9 = TestClass9.class.getName(); - expression = parser.parseExpression("new " + testclass9 + "(42)"); - result = expression.getValue(); - assertThat(result).isExactlyInstanceOf(TestClass9.class); - assertCannotCompile(expression); - } - - @Test - void methodReferenceReflectiveMethodSelectionWithVarargs() { - TestClass10 tc = new TestClass10(); - - // Should call the non varargs version of concat - // (which causes the '::' prefix in test output) - expression = parser.parseExpression("concat('test')"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("::test"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("::test"); - tc.reset(); - - // This will call the varargs concat with an empty array - expression = parser.parseExpression("concat()"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEmpty(); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEmpty(); - tc.reset(); - - // Should call the non varargs version of concat - // (which causes the '::' prefix in test output) - expression = parser.parseExpression("concat2('test')"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("::test"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("::test"); - tc.reset(); - - // This will call the varargs concat with an empty array - expression = parser.parseExpression("concat2()"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEmpty(); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEmpty(); - tc.reset(); - } - - @Test - void methodReferenceVarargs() { - TestClass5 tc = new TestClass5(); - - // varargs string - expression = parser.parseExpression("eleven()"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEmpty(); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEmpty(); - tc.reset(); - - // varargs string - expression = parser.parseExpression("eleven('aaa')"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaa"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaa"); - tc.reset(); - - // varargs string - expression = parser.parseExpression("eleven(stringArray)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaabbbccc"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaabbbccc"); - tc.reset(); - - // varargs string - expression = parser.parseExpression("eleven('aaa','bbb','ccc')"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaabbbccc"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaabbbccc"); - tc.reset(); - - // varargs object - expression = parser.parseExpression("sixteen('aaa','bbb','ccc')"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaabbbccc"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaabbbccc"); - tc.reset(); - - // string array from property in varargs object - expression = parser.parseExpression("sixteen(seventeen)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaabbbccc"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaabbbccc"); - tc.reset(); - - // string array from variable in varargs object - expression = parser.parseExpression("sixteen(stringArray)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaabbbccc"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaabbbccc"); - tc.reset(); - - // string array in varargs object with other parameter - expression = parser.parseExpression("eighteen('AAA', stringArray)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("AAA::aaabbbccc"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("AAA::aaabbbccc"); - tc.reset(); - - // varargs int - expression = parser.parseExpression("twelve(1,2,3)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.i).isEqualTo(6); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.i).isEqualTo(6); - tc.reset(); - - expression = parser.parseExpression("twelve(1)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.i).isEqualTo(1); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.i).isEqualTo(1); - tc.reset(); - - // one string then varargs string - expression = parser.parseExpression("thirteen('aaa','bbb','ccc')"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaa::bbbccc"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaa::bbbccc"); - tc.reset(); - - // nothing passed to varargs parameter - expression = parser.parseExpression("thirteen('aaa')"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaa::"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaa::"); - tc.reset(); - - // nested arrays - expression = parser.parseExpression("fourteen('aaa',stringArray,stringArray)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaa::{aaabbbccc}{aaabbbccc}"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaa::{aaabbbccc}{aaabbbccc}"); - tc.reset(); - - // nested primitive array - expression = parser.parseExpression("fifteen('aaa',intArray,intArray)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaa::{112233}{112233}"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("aaa::{112233}{112233}"); - tc.reset(); - - // varargs boolean - expression = parser.parseExpression("arrayz(true,true,false)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("truetruefalse"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("truetruefalse"); - tc.reset(); - - expression = parser.parseExpression("arrayz(true)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("true"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("true"); - tc.reset(); - - // varargs short - expression = parser.parseExpression("arrays(s1,s2,s3)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("123"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("123"); - tc.reset(); - - expression = parser.parseExpression("arrays(s1)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1"); - tc.reset(); - - // varargs double - expression = parser.parseExpression("arrayd(1.0d,2.0d,3.0d)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1.02.03.0"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1.02.03.0"); - tc.reset(); - - expression = parser.parseExpression("arrayd(1.0d)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1.0"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1.0"); - tc.reset(); - - // varargs long - expression = parser.parseExpression("arrayj(l1,l2,l3)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("123"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("123"); - tc.reset(); - - expression = parser.parseExpression("arrayj(l1)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1"); - tc.reset(); - - // varargs char - expression = parser.parseExpression("arrayc(c1,c2,c3)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("abc"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("abc"); - tc.reset(); - - expression = parser.parseExpression("arrayc(c1)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("a"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("a"); - tc.reset(); - - // varargs byte - expression = parser.parseExpression("arrayb(b1,b2,b3)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("656667"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("656667"); - tc.reset(); - - expression = parser.parseExpression("arrayb(b1)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("65"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("65"); - tc.reset(); - - // varargs float - expression = parser.parseExpression("arrayf(f1,f2,f3)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1.02.03.0"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1.02.03.0"); - tc.reset(); - - expression = parser.parseExpression("arrayf(f1)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1.0"); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("1.0"); - tc.reset(); - } - - @Test // gh-27421 - void nullSafeInvocationOfNonStaticVoidMethod() { - // non-static method, no args, void return - expression = parser.parseExpression("new %s()?.one()".formatted(TestClass5.class.getName())); - - assertCannotCompile(expression); - - TestClass5._i = 0; - assertThat(expression.getValue()).isNull(); - assertThat(TestClass5._i).isEqualTo(1); - - TestClass5._i = 0; - assertCanCompile(expression); - assertThat(expression.getValue()).isNull(); - assertThat(TestClass5._i).isEqualTo(1); - } - - @Test // gh-27421 - void nullSafeInvocationOfStaticVoidMethod() { - // static method, no args, void return - expression = parser.parseExpression("T(%s)?.two()".formatted(TestClass5.class.getName())); - - assertCannotCompile(expression); - - TestClass5._i = 0; - assertThat(expression.getValue()).isNull(); - assertThat(TestClass5._i).isEqualTo(1); - - TestClass5._i = 0; - assertCanCompile(expression); - assertThat(expression.getValue()).isNull(); - assertThat(TestClass5._i).isEqualTo(1); - } - - @Test // gh-27421 - void nullSafeInvocationOfNonStaticVoidWrapperMethod() { - // non-static method, no args, Void return - expression = parser.parseExpression("new %s()?.oneVoidWrapper()".formatted(TestClass5.class.getName())); - - assertCannotCompile(expression); - - TestClass5._i = 0; - assertThat(expression.getValue()).isNull(); - assertThat(TestClass5._i).isEqualTo(1); - - TestClass5._i = 0; - assertCanCompile(expression); - assertThat(expression.getValue()).isNull(); - assertThat(TestClass5._i).isEqualTo(1); - } - - @Test // gh-27421 - void nullSafeInvocationOfStaticVoidWrapperMethod() { - // static method, no args, Void return - expression = parser.parseExpression("T(%s)?.twoVoidWrapper()".formatted(TestClass5.class.getName())); - - assertCannotCompile(expression); - - TestClass5._i = 0; - assertThat(expression.getValue()).isNull(); - assertThat(TestClass5._i).isEqualTo(1); - - TestClass5._i = 0; - assertCanCompile(expression); - assertThat(expression.getValue()).isNull(); - assertThat(TestClass5._i).isEqualTo(1); - } - - @Test - void methodReference() { - TestClass5 tc = new TestClass5(); - - // non-static method, no args, void return - expression = parser.parseExpression("one()"); - assertCannotCompile(expression); - expression.getValue(tc); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.i).isEqualTo(1); - tc.reset(); - - // static method, no args, void return - expression = parser.parseExpression("two()"); - assertCannotCompile(expression); - expression.getValue(tc); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(TestClass5._i).isEqualTo(1); - tc.reset(); - - // non-static method, reference type return - expression = parser.parseExpression("three()"); - assertCannotCompile(expression); - expression.getValue(tc); - assertCanCompile(expression); - tc.reset(); - assertThat(expression.getValue(tc)).isEqualTo("hello"); - tc.reset(); - - // non-static method, primitive type return - expression = parser.parseExpression("four()"); - assertCannotCompile(expression); - expression.getValue(tc); - assertCanCompile(expression); - tc.reset(); - assertThat(expression.getValue(tc)).isEqualTo(3277700L); - tc.reset(); - - // static method, reference type return - expression = parser.parseExpression("five()"); - assertCannotCompile(expression); - expression.getValue(tc); - assertCanCompile(expression); - tc.reset(); - assertThat(expression.getValue(tc)).isEqualTo("hello"); - tc.reset(); - - // static method, primitive type return - expression = parser.parseExpression("six()"); - assertCannotCompile(expression); - expression.getValue(tc); - assertCanCompile(expression); - tc.reset(); - assertThat(expression.getValue(tc)).isEqualTo(3277700L); - tc.reset(); - - // non-static method, one parameter of reference type - expression = parser.parseExpression("seven(\"foo\")"); - assertCannotCompile(expression); - expression.getValue(tc); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("foo"); - tc.reset(); - - // static method, one parameter of reference type - expression = parser.parseExpression("eight(\"bar\")"); - assertCannotCompile(expression); - expression.getValue(tc); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(TestClass5._s).isEqualTo("bar"); - tc.reset(); - - // non-static method, one parameter of primitive type - expression = parser.parseExpression("nine(231)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(tc.i).isEqualTo(231); - tc.reset(); - - // static method, one parameter of primitive type - expression = parser.parseExpression("ten(111)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertCanCompile(expression); - tc.reset(); - expression.getValue(tc); - assertThat(TestClass5._i).isEqualTo(111); - tc.reset(); - - // method that gets type converted parameters - - // Converting from an int to a string - expression = parser.parseExpression("seven(123)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("123"); - assertCannotCompile(expression); // Uncompilable as argument conversion is occurring - - Expression expression = parser.parseExpression("'abcd'.substring(index1,index2)"); - String resultI = expression.getValue(new TestClass1(), String.class); - assertCanCompile(expression); - String resultC = expression.getValue(new TestClass1(), String.class); - assertThat(resultI).isEqualTo("bc"); - assertThat(resultC).isEqualTo("bc"); - - // Converting from an int to a Number - expression = parser.parseExpression("takeNumber(123)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("123"); - tc.reset(); - assertCanCompile(expression); // The generated code should include boxing of the int to a Number - expression.getValue(tc); - assertThat(tc.s).isEqualTo("123"); - - // Passing a subtype - expression = parser.parseExpression("takeNumber(T(Integer).valueOf(42))"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("42"); - tc.reset(); - assertCanCompile(expression); // The generated code should include boxing of the int to a Number - expression.getValue(tc); - assertThat(tc.s).isEqualTo("42"); - - // Passing a subtype - expression = parser.parseExpression("takeString(T(Integer).valueOf(42))"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("42"); - tc.reset(); - assertCannotCompile(expression); // method takes a string and we are passing an Integer - } - - @Test - void errorHandling() { - TestClass5 tc = new TestClass5(); - - // changing target - - // from primitive array to reference type array - int[] is = new int[] {1,2,3}; - String[] strings = new String[] {"a","b","c"}; - expression = parser.parseExpression("[1]"); - assertThat(expression.getValue(is)).isEqualTo(2); - assertCanCompile(expression); - assertThat(expression.getValue(is)).isEqualTo(2); - assertThatExceptionOfType(SpelEvaluationException.class) - .isThrownBy(() -> expression.getValue(strings)) - .withCauseInstanceOf(ClassCastException.class); - SpelCompiler.revertToInterpreted(expression); - assertThat(expression.getValue(strings)).isEqualTo("b"); - assertCanCompile(expression); - assertThat(expression.getValue(strings)).isEqualTo("b"); - - - tc.field = "foo"; - expression = parser.parseExpression("seven(field)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("foo"); - assertCanCompile(expression); - tc.reset(); - tc.field="bar"; - expression.getValue(tc); - - // method with changing parameter types (change reference type) - tc.obj = "foo"; - expression = parser.parseExpression("seven(obj)"); - assertCannotCompile(expression); - expression.getValue(tc); - assertThat(tc.s).isEqualTo("foo"); - assertCanCompile(expression); - tc.reset(); - tc.obj=42; - assertThatExceptionOfType(SpelEvaluationException.class) - .isThrownBy(() -> expression.getValue(tc)) - .withCauseInstanceOf(ClassCastException.class); - - - // method with changing target - expression = parser.parseExpression("#root.charAt(0)"); - assertThat(expression.getValue("abc")).isEqualTo('a'); - assertCanCompile(expression); - assertThatExceptionOfType(SpelEvaluationException.class) - .isThrownBy(() -> expression.getValue(42)) - .withCauseInstanceOf(ClassCastException.class); - } - - @Test - void methodReference_staticMethod() { - Expression expression = parser.parseExpression("T(Integer).valueOf(42)"); - int resultI = expression.getValue(new TestClass1(), int.class); - assertCanCompile(expression); - int resultC = expression.getValue(new TestClass1(), int.class); - assertThat(resultI).isEqualTo(42); - assertThat(resultC).isEqualTo(42); - } - - @Test - void methodReference_literalArguments_int() { - Expression expression = parser.parseExpression("'abcd'.substring(1,3)"); - String resultI = expression.getValue(new TestClass1(), String.class); - assertCanCompile(expression); - String resultC = expression.getValue(new TestClass1(), String.class); - assertThat(resultI).isEqualTo("bc"); - assertThat(resultC).isEqualTo("bc"); - } - - @Test - void methodReference_simpleInstanceMethodNoArg() { - Expression expression = parser.parseExpression("toString()"); - String resultI = expression.getValue(42, String.class); - assertCanCompile(expression); - String resultC = expression.getValue(42, String.class); - assertThat(resultI).isEqualTo("42"); - assertThat(resultC).isEqualTo("42"); - } - - @Test - void methodReference_simpleInstanceMethodNoArgReturnPrimitive() { - expression = parser.parseExpression("intValue()"); - int resultI = expression.getValue(42, int.class); - assertThat(resultI).isEqualTo(42); - assertCanCompile(expression); - int resultC = expression.getValue(42, int.class); - assertThat(resultC).isEqualTo(42); - } - - @Test - void methodReference_simpleInstanceMethodOneArgReturnPrimitive1() { - Expression expression = parser.parseExpression("indexOf('b')"); - int resultI = expression.getValue("abc", int.class); - assertCanCompile(expression); - int resultC = expression.getValue("abc", int.class); - assertThat(resultI).isEqualTo(1); - assertThat(resultC).isEqualTo(1); - } - - @Test - void methodReference_simpleInstanceMethodOneArgReturnPrimitive2() { - expression = parser.parseExpression("charAt(2)"); - char resultI = expression.getValue("abc", char.class); - assertThat(resultI).isEqualTo('c'); - assertCanCompile(expression); - char resultC = expression.getValue("abc", char.class); - assertThat(resultC).isEqualTo('c'); - } - - @Test - void compoundExpression() { - Payload payload = new Payload(); - expression = parser.parseExpression("DR[0]"); - assertThat(expression.getValue(payload).toString()).isEqualTo("instanceof Two"); - assertCanCompile(expression); - assertThat(expression.getValue(payload).toString()).isEqualTo("instanceof Two"); - ast = getAst(); - assertThat(ast.getExitDescriptor()).isEqualTo("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Two"); - - expression = parser.parseExpression("holder.three"); - assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Three"); - assertCanCompile(expression); - assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Three"); - ast = getAst(); - assertThat(ast.getExitDescriptor()).isEqualTo("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Three"); - - expression = parser.parseExpression("DR[0]"); - assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Two"); - assertCanCompile(expression); - assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Two"); - assertThat(getAst().getExitDescriptor()).isEqualTo("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Two"); - - expression = parser.parseExpression("DR[0].three"); - assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Three"); - assertCanCompile(expression); - assertThat(expression.getValue(payload).getClass().getName()).isEqualTo("org.springframework.expression.spel.SpelCompilationCoverageTests$Three"); - ast = getAst(); - assertThat(ast.getExitDescriptor()).isEqualTo("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Three"); - - expression = parser.parseExpression("DR[0].three.four"); - assertThat(expression.getValue(payload)).isEqualTo(0.04d); - assertCanCompile(expression); - assertThat(expression.getValue(payload)).isEqualTo(0.04d); - assertThat(getAst().getExitDescriptor()).isEqualTo("D"); - } - - @Test - void mixingItUp_indexerOpEqTernary() { - Map m = new HashMap<>(); - m.put("andy","778"); - - expression = parse("['andy']==null?1:2"); - assertThat(expression.getValue(m)).isEqualTo(2); - assertCanCompile(expression); - assertThat(expression.getValue(m)).isEqualTo(2); - m.remove("andy"); - assertThat(expression.getValue(m)).isEqualTo(1); - } - - @Test - void propertyReference() { - TestClass6 tc = new TestClass6(); - - // non-static field - expression = parser.parseExpression("orange"); - assertCannotCompile(expression); - assertThat(expression.getValue(tc)).isEqualTo("value1"); - assertCanCompile(expression); - assertThat(expression.getValue(tc)).isEqualTo("value1"); - - // static field - expression = parser.parseExpression("apple"); - assertCannotCompile(expression); - assertThat(expression.getValue(tc)).isEqualTo("value2"); - assertCanCompile(expression); - assertThat(expression.getValue(tc)).isEqualTo("value2"); - - // non static getter - expression = parser.parseExpression("banana"); - assertCannotCompile(expression); - assertThat(expression.getValue(tc)).isEqualTo("value3"); - assertCanCompile(expression); - assertThat(expression.getValue(tc)).isEqualTo("value3"); - - // static getter - expression = parser.parseExpression("plum"); - assertCannotCompile(expression); - assertThat(expression.getValue(tc)).isEqualTo("value4"); - assertCanCompile(expression); - assertThat(expression.getValue(tc)).isEqualTo("value4"); - - // record-style accessor - expression = parser.parseExpression("strawberry"); - assertCannotCompile(expression); - assertThat(expression.getValue(tc)).isEqualTo("value5"); - assertCanCompile(expression); - assertThat(expression.getValue(tc)).isEqualTo("value5"); - } - - @Test - void propertyReferenceVisibility_SPR12771() { - StandardEvaluationContext ctx = new StandardEvaluationContext(); - ctx.setVariable("httpServletRequest", HttpServlet3RequestFactory.getOne()); - // Without a fix compilation was inserting a checkcast to a private type - expression = parser.parseExpression("#httpServletRequest.servletPath"); - assertThat(expression.getValue(ctx)).isEqualTo("wibble"); - assertCanCompile(expression); - assertThat(expression.getValue(ctx)).isEqualTo("wibble"); - } - - @Test - void plusNeedingCheckcast_SPR12426() { - expression = parser.parseExpression("object + ' world'"); - Object v = expression.getValue(new FooObject()); - assertThat(v).isEqualTo("hello world"); - assertCanCompile(expression); - assertThat(v).isEqualTo("hello world"); - - expression = parser.parseExpression("object + ' world'"); - v = expression.getValue(new FooString()); - assertThat(v).isEqualTo("hello world"); - assertCanCompile(expression); - assertThat(v).isEqualTo("hello world"); - } - - @Test - void mixingItUp_propertyAccessIndexerOpLtTernaryRootNull() { - Payload payload = new Payload(); - - expression = parser.parseExpression("DR[0].three"); - Object v = expression.getValue(payload); - assertThat(getAst().getExitDescriptor()).isEqualTo("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Three"); - - Expression expression = parser.parseExpression("DR[0].three.four lt 0.1d?#root:null"); - v = expression.getValue(payload); - - SpelExpression sExpr = (SpelExpression) expression; - Ternary ternary = (Ternary) sExpr.getAST(); - OpLT oplt = (OpLT) ternary.getChild(0); - CompoundExpression cExpr = (CompoundExpression) oplt.getLeftOperand(); - String cExprExitDescriptor = cExpr.getExitDescriptor(); - assertThat(cExprExitDescriptor).isEqualTo("D"); - assertThat(oplt.getExitDescriptor()).isEqualTo("Z"); - - assertCanCompile(expression); - Object vc = expression.getValue(payload); - assertThat(v).isEqualTo(payload); - assertThat(vc).isEqualTo(payload); - payload.DR[0].three.four = 0.13d; - vc = expression.getValue(payload); - assertThat(vc).isNull(); - } - - @Test - void variantGetter() { - Payload2Holder holder = new Payload2Holder(); - StandardEvaluationContext ctx = new StandardEvaluationContext(); - ctx.addPropertyAccessor(new MyPropertyAccessor()); - expression = parser.parseExpression("payload2.var1"); - Object v = expression.getValue(ctx,holder); - assertThat(v).isEqualTo("abc"); - - assertCanCompile(expression); - v = expression.getValue(ctx,holder); - assertThat(v).isEqualTo("abc"); - } - - @Test - void compilerWithGenerics_12040() { - expression = parser.parseExpression("payload!=2"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class)).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class)).isFalse(); - - expression = parser.parseExpression("2!=payload"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class)).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class)).isFalse(); - - expression = parser.parseExpression("payload!=6L"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4L), Boolean.class)).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(6L), Boolean.class)).isFalse(); - - expression = parser.parseExpression("payload==2"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class)).isFalse(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class)).isTrue(); - - expression = parser.parseExpression("2==payload"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class)).isFalse(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class)).isTrue(); - - expression = parser.parseExpression("payload==6L"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4L), Boolean.class)).isFalse(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(6L), Boolean.class)).isTrue(); - - expression = parser.parseExpression("2==payload"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4), Boolean.class)).isFalse(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(2), Boolean.class)).isTrue(); - - expression = parser.parseExpression("payload/2"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(2); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(6))).isEqualTo(3); - - expression = parser.parseExpression("100/payload"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(25); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(10))).isEqualTo(10); - - expression = parser.parseExpression("payload+2"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(6); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(6))).isEqualTo(8); - - expression = parser.parseExpression("100+payload"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(104); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(10))).isEqualTo(110); - - expression = parser.parseExpression("payload-2"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(2); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(6))).isEqualTo(4); - - expression = parser.parseExpression("100-payload"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(96); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(10))).isEqualTo(90); - - expression = parser.parseExpression("payload*2"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(8); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(6))).isEqualTo(12); - - expression = parser.parseExpression("100*payload"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4))).isEqualTo(400); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(10))).isEqualTo(1000); - - expression = parser.parseExpression("payload/2L"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4L))).isEqualTo(2L); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(6L))).isEqualTo(3L); - - expression = parser.parseExpression("100L/payload"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4L))).isEqualTo(25L); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(10L))).isEqualTo(10L); - - expression = parser.parseExpression("payload/2f"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4f))).isEqualTo(2f); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(6f))).isEqualTo(3f); - - expression = parser.parseExpression("100f/payload"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4f))).isEqualTo(25f); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(10f))).isEqualTo(10f); - - expression = parser.parseExpression("payload/2d"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4d))).isEqualTo(2d); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(6d))).isEqualTo(3d); - - expression = parser.parseExpression("100d/payload"); - assertThat(expression.getValue(new GenericMessageTestHelper<>(4d))).isEqualTo(25d); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper<>(10d))).isEqualTo(10d); - } - - // The new helper class here uses an upper bound on the generic - @Test - void compilerWithGenerics_12040_2() { - expression = parser.parseExpression("payload/2"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(2); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(6))).isEqualTo(3); - - expression = parser.parseExpression("9/payload"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(9))).isEqualTo(1); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(3))).isEqualTo(3); - - expression = parser.parseExpression("payload+2"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(6); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(6))).isEqualTo(8); - - expression = parser.parseExpression("100+payload"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(104); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(10))).isEqualTo(110); - - expression = parser.parseExpression("payload-2"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(2); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(6))).isEqualTo(4); - - expression = parser.parseExpression("100-payload"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(96); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(10))).isEqualTo(90); - - expression = parser.parseExpression("payload*2"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(8); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(6))).isEqualTo(12); - - expression = parser.parseExpression("100*payload"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(4))).isEqualTo(400); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(10))).isEqualTo(1000); - } - - // The other numeric operators - @Test - void compilerWithGenerics_12040_3() { - expression = parser.parseExpression("payload >= 2"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(4), boolean.class)).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isFalse(); - - expression = parser.parseExpression("2 >= payload"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(5), boolean.class)).isFalse(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isTrue(); - - expression = parser.parseExpression("payload > 2"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(4), boolean.class)).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isFalse(); - - expression = parser.parseExpression("2 > payload"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(5), boolean.class)).isFalse(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isTrue(); - - expression = parser.parseExpression("payload <=2"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(6), boolean.class)).isFalse(); - - expression = parser.parseExpression("2 <= payload"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isFalse(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(6), boolean.class)).isTrue(); - - expression = parser.parseExpression("payload < 2"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isTrue(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(6), boolean.class)).isFalse(); - - expression = parser.parseExpression("2 < payload"); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(1), boolean.class)).isFalse(); - assertCanCompile(expression); - assertThat(expression.getValue(new GenericMessageTestHelper2<>(6), boolean.class)).isTrue(); - } - - @Test - void indexerMapAccessor_12045() { - SpelParserConfiguration spc = new SpelParserConfiguration( - SpelCompilerMode.IMMEDIATE,getClass().getClassLoader()); - SpelExpressionParser sep = new SpelExpressionParser(spc); - expression=sep.parseExpression("headers[command]"); - MyMessage root = new MyMessage(); - assertThat(expression.getValue(root)).isEqualTo("wibble"); - // This next call was failing because the isCompilable check in Indexer - // did not check on the key being compilable (and also generateCode in the - // Indexer was missing the optimization that it didn't need necessarily - // need to call generateCode for that accessor) - assertThat(expression.getValue(root)).isEqualTo("wibble"); - assertCanCompile(expression); - - // What about a map key that is an expression - ensure the getKey() is evaluated in the right scope - expression=sep.parseExpression("headers[getKey()]"); - assertThat(expression.getValue(root)).isEqualTo("wobble"); - assertThat(expression.getValue(root)).isEqualTo("wobble"); - - expression=sep.parseExpression("list[getKey2()]"); - assertThat(expression.getValue(root)).isEqualTo("wobble"); - assertThat(expression.getValue(root)).isEqualTo("wobble"); - - expression = sep.parseExpression("ia[getKey2()]"); - assertThat(expression.getValue(root)).isEqualTo(3); - assertThat(expression.getValue(root)).isEqualTo(3); - } - - @Test - void nullComparison_SPR22358() { - SpelParserConfiguration configuration = new SpelParserConfiguration(SpelCompilerMode.OFF, null); - SpelExpressionParser parser = new SpelExpressionParser(configuration); - StandardEvaluationContext ctx = new StandardEvaluationContext(); - ctx.setRootObject(new Reg(1)); - verifyCompilationAndBehaviourWithNull("value>1", parser, ctx ); - verifyCompilationAndBehaviourWithNull("value<1", parser, ctx ); - verifyCompilationAndBehaviourWithNull("value>=1", parser, ctx ); - verifyCompilationAndBehaviourWithNull("value<=1", parser, ctx ); - - verifyCompilationAndBehaviourWithNull2("value>value2", parser, ctx ); - verifyCompilationAndBehaviourWithNull2("value=value2", parser, ctx ); - verifyCompilationAndBehaviourWithNull2("value<=value2", parser, ctx ); - - verifyCompilationAndBehaviourWithNull("valueD>1.0d", parser, ctx ); - verifyCompilationAndBehaviourWithNull("valueD<1.0d", parser, ctx ); - verifyCompilationAndBehaviourWithNull("valueD>=1.0d", parser, ctx ); - verifyCompilationAndBehaviourWithNull("valueD<=1.0d", parser, ctx ); - - verifyCompilationAndBehaviourWithNull2("valueD>valueD2", parser, ctx ); - verifyCompilationAndBehaviourWithNull2("valueD=valueD2", parser, ctx ); - verifyCompilationAndBehaviourWithNull2("valueD<=valueD2", parser, ctx ); - - verifyCompilationAndBehaviourWithNull("valueL>1L", parser, ctx ); - verifyCompilationAndBehaviourWithNull("valueL<1L", parser, ctx ); - verifyCompilationAndBehaviourWithNull("valueL>=1L", parser, ctx ); - verifyCompilationAndBehaviourWithNull("valueL<=1L", parser, ctx ); - - verifyCompilationAndBehaviourWithNull2("valueL>valueL2", parser, ctx ); - verifyCompilationAndBehaviourWithNull2("valueL=valueL2", parser, ctx ); - verifyCompilationAndBehaviourWithNull2("valueL<=valueL2", parser, ctx ); - - verifyCompilationAndBehaviourWithNull("valueF>1.0f", parser, ctx ); - verifyCompilationAndBehaviourWithNull("valueF<1.0f", parser, ctx ); - verifyCompilationAndBehaviourWithNull("valueF>=1.0f", parser, ctx ); - verifyCompilationAndBehaviourWithNull("valueF<=1.0f", parser, ctx ); - - verifyCompilationAndBehaviourWithNull("valueF>valueF2", parser, ctx ); - verifyCompilationAndBehaviourWithNull("valueF=valueF2", parser, ctx ); - verifyCompilationAndBehaviourWithNull("valueF<=valueF2", parser, ctx ); - } - - private void verifyCompilationAndBehaviourWithNull(String expressionText, SpelExpressionParser parser, StandardEvaluationContext ctx) { - Reg r = (Reg)ctx.getRootObject().getValue(); - r.setValue2(1); // having a value in value2 fields will enable compilation to succeed, then can switch it to null - SpelExpression fast = (SpelExpression) parser.parseExpression(expressionText); - SpelExpression slow = (SpelExpression) parser.parseExpression(expressionText); - fast.getValue(ctx); - assertThat(fast.compileExpression()).isTrue(); - r.setValue2(null); - // try the numbers 0,1,2,null - for (int i = 0; i < 4; i++) { - r.setValue(i < 3 ? i : null); - boolean slowResult = (Boolean)slow.getValue(ctx); - boolean fastResult = (Boolean)fast.getValue(ctx); - assertThat(fastResult).as("Differing results: expression=" + expressionText + - " value=" + r.getValue() + " slow=" + slowResult + " fast="+fastResult).isEqualTo(slowResult); - } - } - - private void verifyCompilationAndBehaviourWithNull2(String expressionText, SpelExpressionParser parser, StandardEvaluationContext ctx) { - SpelExpression fast = (SpelExpression) parser.parseExpression(expressionText); - SpelExpression slow = (SpelExpression) parser.parseExpression(expressionText); - fast.getValue(ctx); - assertThat(fast.compileExpression()).isTrue(); - Reg r = (Reg)ctx.getRootObject().getValue(); - // try the numbers 0,1,2,null - for (int i = 0; i < 4; i++) { - r.setValue(i < 3 ? i : null); - boolean slowResult = (Boolean)slow.getValue(ctx); - boolean fastResult = (Boolean)fast.getValue(ctx); - assertThat(fastResult).as("Differing results: expression=" + expressionText + - " value=" + r.getValue() + " slow=" + slowResult + " fast="+fastResult).isEqualTo(slowResult); - } - } - - @Test - void repeatedCompilation() throws Exception { - // Verifying that after a number of compilations, the classloaders - // used to load the compiled expressions are discarded/replaced. - // See SpelCompiler.loadClass() - Field f = SpelExpression.class.getDeclaredField("compiledAst"); - Set classloadersUsed = new HashSet<>(); - for (int i = 0; i < 1500; i++) { // 1500 is greater than SpelCompiler.CLASSES_DEFINED_LIMIT - expression = parser.parseExpression("4 + 5"); - assertThat((int) expression.getValue(Integer.class)).isEqualTo(9); - assertCanCompile(expression); - f.setAccessible(true); - CompiledExpression cEx = (CompiledExpression) f.get(expression); - classloadersUsed.add(cEx.getClass().getClassLoader()); - assertThat((int) expression.getValue(Integer.class)).isEqualTo(9); - } - assertThat(classloadersUsed.size()).isGreaterThan(1); - } - - // Helper methods private SpelNodeImpl getAst() { @@ -6448,6 +6458,34 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { assertThat(Modifier.isPublic(clazz.getModifiers())).as("%s must be private", clazz.getName()).isFalse(); } + public static String concat(String a, String b) { + return a + b; + } + + public static String concat2(Object... args) { + return Arrays.stream(args) + .map(Objects::toString) + .collect(Collectors.joining()); + } + + public static String join(String... strings) { + StringBuilder buf = new StringBuilder(); + for (String string : strings) { + buf.append(string); + } + return buf.toString(); + } + + private static String voidMethodInvokedWith; + + public static Void voidWrapperMethod(String str) { + voidMethodInvokedWith = str; + return null; + } + + public static void voidMethod(String str) { + voidMethodInvokedWith = str; + } // Nested types @@ -6603,6 +6641,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } } + public static class FooObjectHolder { private FooObject foo = new FooObject(); @@ -6612,6 +6651,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } } + public static class FooObject { static boolean doFooInvoked = false; @@ -7477,6 +7517,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } } + private interface PrivateInterface { String getMessage(); @@ -7484,6 +7525,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { String getIndex(int index); } + private static class PrivateSubclass extends PublicSuperclass implements PublicInterface, PrivateInterface { @Override @@ -7512,6 +7554,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } } + // Must be public with public fields/properties. public static class RootContextWithIndexedProperties { public int[] intArray; @@ -7523,6 +7566,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { public Person person; } + /** * Type that can be indexed by an int or an Integer and whose indexed values * are enums. @@ -7550,6 +7594,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } } + /** * {@link CompilableIndexAccessor} that knows how to index into {@link Colors}. */ @@ -7560,6 +7605,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } } + /** * Type that can be indexed by an enum and whose indexed values are primitive * integers. @@ -7571,6 +7617,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } } + /** * {@link CompilableIndexAccessor} that knows how to index into {@link ColorOrdinals}. */ @@ -7581,6 +7628,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { } } + /** * Manually implemented {@link CompilableIndexAccessor} that knows how to * index into {@link FruitMap} for reading, writing, and compilation.