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 aed1d5f762)
This commit is contained in:
Sam Brannen 2024-05-28 10:19:15 +02:00
parent 0f04052ba1
commit 2384474615
2 changed files with 47 additions and 3 deletions

View File

@ -285,9 +285,7 @@ public class Indexer extends SpelNodeImpl {
mv.visitLdcInsn(mapKeyName); mv.visitLdcInsn(mapKeyName);
} }
else { else {
cf.enterCompilationScope(); generateIndexCode(mv, cf, index, Object.class);
index.generateCode(mv, cf);
cf.exitCompilationScope();
} }
mv.visitMethodInsn( mv.visitMethodInsn(
INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);

View File

@ -552,6 +552,52 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object"); assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
} }
@Test // gh-32903
void indexIntoMapUsingPrimitiveLiteral() {
Map<Object, String> map = new HashMap<>();
map.put(false, "0"); // BooleanLiteral
map.put(1, "ABC"); // IntLiteral
map.put(2L, "XYZ"); // LongLiteral
map.put(9.99F, "~10"); // FloatLiteral
map.put(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) { private String stringify(Object object) {
Stream<? extends Object> stream; Stream<? extends Object> stream;
if (object instanceof Collection) { if (object instanceof Collection) {