From e9de426eb5ca14b354073ec35f10fa0cdfeb1791 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Tue, 28 May 2024 10:19:15 +0200 Subject: [PATCH] Support compilation of map indexing with primitive in SpEL Prior to this commit, the Spring Expression Language (SpEL) failed to compile an expression that indexed into a Map using a primitive literal (boolean, int, long, float, or double). This commit adds support for compilation of such expressions by ensuring that primitive literals are boxed into their corresponding wrapper types in the compiled bytecode. Closes gh-32903 --- .../expression/spel/ast/Indexer.java | 4 +- .../spel/SpelCompilationCoverageTests.java | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java index e96c858c966..e6505d0abbf 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java @@ -287,9 +287,7 @@ public class Indexer extends SpelNodeImpl { mv.visitLdcInsn(mapKeyName); } else { - cf.enterCompilationScope(); - index.generateCode(mv, cf); - cf.exitCompilationScope(); + generateIndexCode(mv, cf, index, Object.class); } mv.visitMethodInsn( INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); 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 4df4c4b3122..883f2ee3b3f 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 @@ -697,6 +697,53 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object"); } + @Test // gh-32903 + void indexIntoMapUsingPrimitiveLiteral() { + Map map = Map.of( + false, "0", // BooleanLiteral + 1, "ABC", // IntLiteral + 2L, "XYZ", // LongLiteral + 9.99F, "~10", // FloatLiteral + 3.14159, "PI" // RealLiteral + ); + context.setVariable("map", map); + + // BooleanLiteral + expression = parser.parseExpression("#map[false]"); + assertThat(expression.getValue(context)).isEqualTo("0"); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo("0"); + assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object"); + + // IntLiteral + expression = parser.parseExpression("#map[1]"); + assertThat(expression.getValue(context)).isEqualTo("ABC"); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo("ABC"); + assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object"); + + // LongLiteral + expression = parser.parseExpression("#map[2L]"); + assertThat(expression.getValue(context)).isEqualTo("XYZ"); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo("XYZ"); + assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object"); + + // FloatLiteral + expression = parser.parseExpression("#map[9.99F]"); + assertThat(expression.getValue(context)).isEqualTo("~10"); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo("~10"); + assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object"); + + // RealLiteral + expression = parser.parseExpression("#map[3.14159]"); + assertThat(expression.getValue(context)).isEqualTo("PI"); + assertCanCompile(expression); + assertThat(expression.getValue(context)).isEqualTo("PI"); + assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object"); + } + private String stringify(Object object) { Stream stream; if (object instanceof Collection collection) {