Browse Source

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 e9de426eb5)
pull/33048/head
Sam Brannen 2 years ago
parent
commit
aed1d5f762
  1. 4
      spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java
  2. 47
      spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java

4
spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java

@ -283,9 +283,7 @@ public class Indexer extends SpelNodeImpl { @@ -283,9 +283,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);

47
spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java

@ -554,6 +554,53 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { @@ -554,6 +554,53 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
}
@Test // gh-32903
void indexIntoMapUsingPrimitiveLiteral() {
Map<Object, String> 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<? extends Object> stream;
if (object instanceof Collection<?> collection) {

Loading…
Cancel
Save