Skip to content

Commit 89d4b6f

Browse files
LeMikaelFsbrannen
authored andcommitted
Fix array wrapping in varargs invocations.
1 parent 92f54da commit 89d4b6f

File tree

3 files changed

+98
-11
lines changed

3 files changed

+98
-11
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java

+13-4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import org.springframework.util.CollectionUtils;
3737
import org.springframework.util.MethodInvoker;
3838

39+
import static org.springframework.util.ObjectUtils.isArray;
40+
3941
/**
4042
* Utility methods used by the reflection resolver code to discover the appropriate
4143
* methods, constructors, and fields that should be used in expressions.
@@ -457,14 +459,18 @@ private static boolean isFirstEntryInArray(Object value, @Nullable Object possib
457459
* @return a repackaged array of arguments where any varargs setup has been done
458460
*/
459461
public static Object[] setupArgumentsForVarargsInvocation(Class<?>[] requiredParameterTypes, Object... args) {
460-
// Check if array already built for final argument
461462
int parameterCount = requiredParameterTypes.length;
463+
Assert.notEmpty(requiredParameterTypes, "Required parameter types must not be empty");
464+
465+
Class<?> lastRequiredParameterType = requiredParameterTypes[parameterCount - 1];
466+
Assert.isTrue(lastRequiredParameterType.isArray(), "Method must be varargs");
467+
462468
int argumentCount = args.length;
469+
Object lastArgument = argumentCount > 0 ? args[argumentCount - 1] : null;
463470

464471
// Check if repackaging is needed...
465472
if (parameterCount != args.length ||
466-
requiredParameterTypes[parameterCount - 1] !=
467-
(args[argumentCount - 1] != null ? args[argumentCount - 1].getClass() : null)) {
473+
(!isArray(lastArgument) && differentTypes(lastRequiredParameterType, lastArgument))) {
468474

469475
// Create an array for the leading arguments plus the varargs array argument.
470476
Object[] newArgs = new Object[parameterCount];
@@ -477,7 +483,7 @@ public static Object[] setupArgumentsForVarargsInvocation(Class<?>[] requiredPar
477483
if (argumentCount >= parameterCount) {
478484
varargsArraySize = argumentCount - (parameterCount - 1);
479485
}
480-
Class<?> componentType = requiredParameterTypes[parameterCount - 1].componentType();
486+
Class<?> componentType = lastRequiredParameterType.componentType();
481487
Object varargsArray = Array.newInstance(componentType, varargsArraySize);
482488
for (int i = 0; i < varargsArraySize; i++) {
483489
Array.set(varargsArray, i, args[parameterCount - 1 + i]);
@@ -489,6 +495,9 @@ public static Object[] setupArgumentsForVarargsInvocation(Class<?>[] requiredPar
489495
return args;
490496
}
491497

498+
private static boolean differentTypes(Class<?> lastRequiredParameterType, @Nullable Object lastArgument) {
499+
return lastArgument == null || lastRequiredParameterType != lastArgument.getClass();
500+
}
492501

493502
/**
494503
* Arguments match kinds.

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

+15
Original file line numberDiff line numberDiff line change
@@ -4994,6 +4994,17 @@ void methodReferenceVarargs() {
49944994
assertThat(tc.s).isEqualTo("aaabbbccc");
49954995
tc.reset();
49964996

4997+
expression = parser.parseExpression("sixteen(seventeen)");
4998+
assertCannotCompile(expression);
4999+
expression.getValue(tc);
5000+
assertThat(tc.s).isEqualTo("aaabbbccc");
5001+
assertCanCompile(expression);
5002+
tc.reset();
5003+
// see TODO below
5004+
// expression.getValue(tc);
5005+
// assertThat(tc.s).isEqualTo("aaabbbccc");
5006+
// tc.reset();
5007+
49975008
// TODO Determine why the String[] is passed as the first element of the Object... varargs array instead of the entire varargs array.
49985009
// expression = parser.parseExpression("sixteen(stringArray)");
49995010
// assertCantCompile(expression);
@@ -6620,6 +6631,10 @@ public void concat2(Object... vargs) {
66206631
}
66216632
}
66226633
}
6634+
6635+
public String[] seventeen() {
6636+
return new String[] { "aaa", "bbb", "ccc" };
6637+
}
66236638
}
66246639

66256640

spring-expression/src/test/java/org/springframework/expression/spel/support/ReflectionHelperTests.java

+70-7
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040

4141
import static org.assertj.core.api.Assertions.assertThat;
4242
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
43+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
44+
import static org.assertj.core.api.InstanceOfAssertFactories.array;
4345
import static org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMatchKind.CLOSE;
4446
import static org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMatchKind.EXACT;
4547
import static org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMatchKind.REQUIRES_CONVERSION;
@@ -252,14 +254,75 @@ void convertAllArguments() throws Exception {
252254

253255
@Test
254256
void setupArgumentsForVarargsInvocation() {
255-
Object[] newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
256-
new Class<?>[] {String[].class}, "a", "b", "c");
257+
Object[] newArray;
257258

258-
assertThat(newArray).hasSize(1);
259-
Object firstParam = newArray[0];
260-
assertThat(firstParam.getClass().componentType()).isEqualTo(String.class);
261-
Object[] firstParamArray = (Object[]) firstParam;
262-
assertThat(firstParamArray).containsExactly("a", "b", "c");
259+
newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
260+
new Class<?>[] {String[].class}, "a", "b", "c");
261+
assertThat(newArray)
262+
.singleElement()
263+
.asInstanceOf(array(String[].class))
264+
.containsExactly("a", "b", "c");
265+
266+
newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
267+
new Class<?>[] { Object[].class }, "a", "b", "c");
268+
assertThat(newArray)
269+
.singleElement()
270+
.asInstanceOf(array(Object[].class))
271+
.containsExactly("a", "b", "c");
272+
273+
newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
274+
new Class<?>[] { Integer.class, Integer.class, String[].class }, 123, 456, "a", "b", "c");
275+
assertThat(newArray)
276+
.satisfiesExactly(
277+
i -> assertThat(i).isEqualTo(123),
278+
i -> assertThat(i).isEqualTo(456),
279+
i -> assertThat(i).asInstanceOf(array(String[].class)).containsExactly("a", "b", "c"));
280+
281+
newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
282+
new Class<?>[] { String[].class });
283+
assertThat(newArray)
284+
.singleElement()
285+
.asInstanceOf(array(String[].class))
286+
.isEmpty();
287+
288+
newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
289+
new Class<?>[] { String[].class }, new Object[] { new String[] { "a", "b", "c" } });
290+
assertThat(newArray)
291+
.singleElement()
292+
.asInstanceOf(array(String[].class))
293+
.containsExactly("a", "b", "c");
294+
295+
newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
296+
new Class<?>[] { Object[].class }, new Object[] { new String[] { "a", "b", "c" } });
297+
assertThat(newArray)
298+
.singleElement()
299+
.asInstanceOf(array(Object[].class))
300+
.containsExactly("a", "b", "c");
301+
302+
newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
303+
new Class<?>[] { String[].class }, "a");
304+
assertThat(newArray)
305+
.singleElement()
306+
.asInstanceOf(array(String[].class))
307+
.containsExactly("a");
308+
309+
310+
newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
311+
new Class<?>[] { String[].class }, new Object[]{null});
312+
assertThat(newArray)
313+
.singleElement()
314+
.asInstanceOf(array(String[].class))
315+
.singleElement()
316+
.isNull();
317+
318+
assertThatThrownBy(() -> ReflectionHelper.setupArgumentsForVarargsInvocation(
319+
new Class<?>[] { Integer.class, Integer.class }, 123, 456))
320+
.isInstanceOf(IllegalArgumentException.class)
321+
.hasMessage("Method must be varargs");
322+
323+
assertThatThrownBy(() -> ReflectionHelper.setupArgumentsForVarargsInvocation(new Class[] {}, "a", "b", "c"))
324+
.isInstanceOf(IllegalArgumentException.class)
325+
.hasMessage("Required parameter types must not be empty");
263326
}
264327

265328
@Test

0 commit comments

Comments
 (0)