From aed1d5f76271dd8b4082d77a1b42e258d1ad8c86 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 (cherry picked from commit e9de426eb5ca14b354073ec35f10fa0cdfeb1791) --- .../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 eabf26682ead..9dc12f91fdd0 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 @@ -283,9 +283,7 @@ else if (this.indexedType == IndexedType.MAP) { 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 2c9772d288a1..6667cc0cc0ce 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 @@ -554,6 +554,53 @@ void indexIntoListUsingIntegerWrapper() { 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) {