Skip to content

Commit bb64e22

Browse files
committed
Merge branch '6.1.x'
2 parents c6b20c0 + a0f5c16 commit bb64e22

File tree

3 files changed

+44
-14
lines changed

3 files changed

+44
-14
lines changed

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

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,28 @@ private TypedValue executeFunctionViaMethodHandle(ExpressionState state, MethodH
180180
int spelParamCount = functionArgs.length;
181181
int declaredParamCount = declaredParams.parameterCount();
182182

183+
// We don't use methodHandle.isVarargsCollector(), because a MethodHandle created via
184+
// MethodHandle#bindTo() is "never a variable-arity method handle, even if the original
185+
// target method handle was." Thus, we merely assume/suspect that varargs are supported
186+
// if the last parameter type is an array.
183187
boolean isSuspectedVarargs = declaredParams.lastParameterType().isArray();
184188

185-
if (spelParamCount < declaredParamCount || (spelParamCount > declaredParamCount && !isSuspectedVarargs)) {
186-
// incorrect number, including more arguments and not a vararg
187-
// perhaps a subset of arguments was provided but the MethodHandle wasn't bound?
189+
if (isSuspectedVarargs) {
190+
if (spelParamCount < declaredParamCount - 1) {
191+
// Varargs, but the number of provided arguments (potentially 0) is insufficient
192+
// for a varargs invocation for the number of declared parameters.
193+
//
194+
// As stated in the Javadoc for MethodHandle#asVarargsCollector(), "the caller
195+
// must supply, at a minimum, N-1 arguments, where N is the arity of the target."
196+
throw new SpelEvaluationException(SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION,
197+
this.name, spelParamCount, (declaredParamCount - 1) + " or more");
198+
}
199+
}
200+
else if (spelParamCount != declaredParamCount) {
201+
// Incorrect number and not varargs. Perhaps a subset of arguments was provided,
202+
// but the MethodHandle wasn't bound?
188203
throw new SpelEvaluationException(SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION,
189-
this.name, functionArgs.length, declaredParamCount);
204+
this.name, spelParamCount, declaredParamCount);
190205
}
191206

192207
// simplest case: the MethodHandle is fully bound or represents a static method with no params:

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,12 @@ private static void populateMethodHandles(StandardEvaluationContext testContext)
105105
MethodHandle formatObjectVarargs = MethodHandles.lookup().findStatic(TestScenarioCreator.class,
106106
"formatObjectVarargs", MethodType.methodType(String.class, String.class, Object[].class));
107107
testContext.registerFunction("formatObjectVarargs", formatObjectVarargs);
108-
}
108+
109+
// #add(int, int)
110+
MethodHandle add = MethodHandles.lookup().findStatic(TestScenarioCreator.class,
111+
"add", MethodType.methodType(int.class, int.class, int.class));
112+
testContext.registerFunction("add", add);
113+
}
109114

110115
/**
111116
* Register some variables that can be referenced from the tests
@@ -163,4 +168,8 @@ public static String formatObjectVarargs(String format, Object... args) {
163168
return String.format(format, args);
164169
}
165170

171+
public static int add(int x, int y) {
172+
return x + y;
173+
}
174+
166175
}

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

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,13 @@ void functionInvocationWithIncorrectNumberOfArguments() {
5454
evaluateAndCheckError("#reverseInt(1,2)", INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, 0, "reverseInt", 2, 3);
5555
evaluateAndCheckError("#reverseInt(1,2,3,4)", INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, 0, "reverseInt", 4, 3);
5656

57-
// MethodHandle: #message(template, args...)
58-
evaluateAndCheckError("#message()", INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, 0, "message", 0, 2);
59-
evaluateAndCheckError("#message('%s')", INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, 0, "message", 1, 2);
57+
// MethodHandle: #message(String, Object...)
58+
evaluateAndCheckError("#message()", INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, 0, "message", 0, "1 or more");
59+
60+
// MethodHandle: #add(int, int)
61+
evaluateAndCheckError("#add()", INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, 0, "add", 0, 2);
62+
evaluateAndCheckError("#add(1)", INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, 0, "add", 1, 2);
63+
evaluateAndCheckError("#add(1, 2, 3)", INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, 0, "add", 3, 2);
6064
}
6165

6266
@Test
@@ -107,12 +111,6 @@ void functionWithVarargs() {
107111
void functionWithVarargsViaMethodHandle_CurrentlyFailing() {
108112
// Calling 'public static String formatObjectVarargs(String format, Object... args)' -> String.format(format, args)
109113

110-
// No var-args and no conversion necessary
111-
evaluate("#formatObjectVarargs('x')", "x", String.class);
112-
113-
// No var-args but conversion necessary
114-
evaluate("#formatObjectVarargs(9)", "9", String.class);
115-
116114
// No conversion necessary
117115
evaluate("#formatObjectVarargs('x -> %s', new Object[]{''})", "x -> ", String.class);
118116
evaluate("#formatObjectVarargs('x -> %s', new String[]{''})", "x -> ", String.class);
@@ -128,13 +126,21 @@ void functionWithVarargsViaMethodHandle_CurrentlyFailing() {
128126
void functionWithVarargsViaMethodHandle() {
129127
// Calling 'public static String formatObjectVarargs(String format, Object... args)' -> String.format(format, args)
130128

129+
// No var-args and no conversion necessary
130+
evaluate("#formatObjectVarargs('x')", "x", String.class);
131+
132+
// No var-args but conversion necessary
133+
evaluate("#formatObjectVarargs(9)", "9", String.class);
134+
131135
// No conversion necessary
136+
evaluate("#add(3, 4)", 7, Integer.class);
132137
evaluate("#formatObjectVarargs('x -> %s', '')", "x -> ", String.class);
133138
evaluate("#formatObjectVarargs('x -> %s', ' ')", "x -> ", String.class);
134139
evaluate("#formatObjectVarargs('x -> %s', 'a')", "x -> a", String.class);
135140
evaluate("#formatObjectVarargs('x -> %s %s %s', 'a', 'b', 'c')", "x -> a b c", String.class);
136141

137142
// Conversion necessary
143+
evaluate("#add('2', 5.0)", 7, Integer.class);
138144
evaluate("#formatObjectVarargs('x -> %s %s', 2, 3)", "x -> 2 3", String.class);
139145
evaluate("#formatObjectVarargs('x -> %s %s', 'a', 3.0d)", "x -> a 3.0", String.class);
140146

0 commit comments

Comments
 (0)