Skip to content

Commit cda577d

Browse files
committed
Support compilation of array and list indexing with Integer in SpEL
Prior to this commit, the Spring Expression Language (SpEL) failed to compile an expression that indexed into any array or list using an Integer. This commit adds support for compilation of such expressions by ensuring that an Integer is unboxed into an int in the compiled bytecode. See gh-32694 Closes gh-32908
1 parent 8feb842 commit cda577d

File tree

2 files changed

+37
-9
lines changed

2 files changed

+37
-9
lines changed

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

+11-9
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ public void generateCode(MethodVisitor mv, CodeFlow cf) {
221221
cf.loadTarget(mv);
222222
}
223223

224+
SpelNodeImpl index = this.children[0];
225+
224226
if (this.indexedType == IndexedType.ARRAY) {
225227
String exitTypeDescriptor = this.exitTypeDescriptor;
226228
Assert.state(exitTypeDescriptor != null, "Array not compilable without descriptor");
@@ -266,32 +268,27 @@ public void generateCode(MethodVisitor mv, CodeFlow cf) {
266268
}
267269
};
268270

269-
SpelNodeImpl index = this.children[0];
270-
cf.enterCompilationScope();
271-
index.generateCode(mv, cf);
272-
cf.exitCompilationScope();
271+
generateIndexCode(mv, cf, index, int.class);
273272
mv.visitInsn(insn);
274273
}
275274

276275
else if (this.indexedType == IndexedType.LIST) {
277276
mv.visitTypeInsn(CHECKCAST, "java/util/List");
278-
cf.enterCompilationScope();
279-
this.children[0].generateCode(mv, cf);
280-
cf.exitCompilationScope();
277+
generateIndexCode(mv, cf, index, int.class);
281278
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true);
282279
}
283280

284281
else if (this.indexedType == IndexedType.MAP) {
285282
mv.visitTypeInsn(CHECKCAST, "java/util/Map");
286283
// Special case when the key is an unquoted string literal that will be parsed as
287284
// a property/field reference
288-
if ((this.children[0] instanceof PropertyOrFieldReference reference)) {
285+
if ((index instanceof PropertyOrFieldReference reference)) {
289286
String mapKeyName = reference.getName();
290287
mv.visitLdcInsn(mapKeyName);
291288
}
292289
else {
293290
cf.enterCompilationScope();
294-
this.children[0].generateCode(mv, cf);
291+
index.generateCode(mv, cf);
295292
cf.exitCompilationScope();
296293
}
297294
mv.visitMethodInsn(
@@ -328,6 +325,11 @@ else if (this.indexedType == IndexedType.OBJECT) {
328325
cf.pushDescriptor(this.exitTypeDescriptor);
329326
}
330327

328+
private void generateIndexCode(MethodVisitor mv, CodeFlow cf, SpelNodeImpl indexNode, Class<?> indexType) {
329+
String indexDesc = CodeFlow.toDescriptor(indexType);
330+
generateCodeForArgument(mv, cf, indexNode, indexDesc);
331+
}
332+
331333
@Override
332334
public String toStringAST() {
333335
return "[" + getChild(0).toStringAST() + "]";

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

+26
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,32 @@ void indexIntoObject() {
671671
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
672672
}
673673

674+
@Test // gh-32694, gh-32908
675+
void indexIntoArrayUsingIntegerWrapper() {
676+
context.setVariable("array", new int[] {1, 2, 3, 4});
677+
context.setVariable("index", 2);
678+
679+
expression = parser.parseExpression("#array[#index]");
680+
681+
assertThat(expression.getValue(context)).isEqualTo(3);
682+
assertCanCompile(expression);
683+
assertThat(expression.getValue(context)).isEqualTo(3);
684+
assertThat(getAst().getExitDescriptor()).isEqualTo("I");
685+
}
686+
687+
@Test // gh-32694, gh-32908
688+
void indexIntoListUsingIntegerWrapper() {
689+
context.setVariable("list", List.of(1, 2, 3, 4));
690+
context.setVariable("index", 2);
691+
692+
expression = parser.parseExpression("#list[#index]");
693+
694+
assertThat(expression.getValue(context)).isEqualTo(3);
695+
assertCanCompile(expression);
696+
assertThat(expression.getValue(context)).isEqualTo(3);
697+
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
698+
}
699+
674700
private String stringify(Object object) {
675701
Stream<? extends Object> stream;
676702
if (object instanceof Collection<?> collection) {

0 commit comments

Comments
 (0)