SpEL fails to invoke function references whose function handle only consists of varargs parameters.
org.springframework.expression.spel.SpelEvaluationException: EL1023E: A problem occurred whilst attempting to invoke the function 'varArgsFunction': 'Cannot cast [Ljava.lang.String; to java.lang.String'
at org.springframework.expression.spel.ast.FunctionReference.executeFunctionViaMethodHandle(FunctionReference.java:257)
at org.springframework.expression.spel.ast.FunctionReference.getValueInternal(FunctionReference.java:97)
at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:114)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:273)
at test.ReproduceVarArgsBugTest.testVarArgsFunction(ReproduceVarArgsBugTest.java:21)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: java.lang.ClassCastException: Cannot cast [Ljava.lang.String; to java.lang.String
at java.base/java.lang.Class.cast(Class.java:3889)
at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:732)
at java.base/java.lang.invoke.MethodHandleImpl$AsVarargsCollector.invokeWithArguments(MethodHandleImpl.java:535)
at org.springframework.expression.spel.ast.FunctionReference.executeFunctionViaMethodHandle(FunctionReference.java:253)
This seems to be failing because in FunctionReference, methodHandle.invokeWithArguments(functionArgs)
is invoked with functionArgs
being set as Object[] {String[] {"a", "b", "c"}}
. Instead of being wrapped in an Object[]
, functionArgs
should have just been String[] {"a", "b", "c"}
.
Reproduction
Spring Framework: 6.1.16
Here's a simple test class to reproduce it:
package test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class ReproduceVarArgsBugTest {
// this test fails since varArgsFunction only consists of varargs parameters
@Test
void testVarArgsFunction() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
MethodHandle methodHandle = MethodHandles.lookup().findStatic(ReproduceVarArgsBugTest.class,
"varArgsFunction", MethodType.methodType(String.class, String[].class));
context.registerFunction("varArgsFunction", methodHandle);
Assertions.assertEquals("a,b,c", parser.parseExpression("#varArgsFunction('a', 'b', 'c')").getValue(context));
}
public static String varArgsFunction(String... input) {
return String.join(",", input);
}
// this passes because varArgsWithOtherParamFunction has at least one parameter that isn't varargs (i.e. otherParam)
@Test
void testVarArgsWithOtherParamFunction() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
MethodHandle methodHandle = MethodHandles.lookup().findStatic(ReproduceVarArgsBugTest.class,
"varArgsWithOtherParamFunction", MethodType.methodType(String.class, String.class, String[].class));
context.registerFunction("varArgsWithOtherParamFunction", methodHandle);
Assertions.assertEquals("a,b,c", parser.parseExpression("#varArgsWithOtherParamFunction('a', 'b', 'c')").getValue(context));
}
public static String varArgsWithOtherParamFunction(String otherParam, String... input) {
return otherParam + "," + String.join(",", input);
}
}
Comment From: sbrannen
Thanks for reporting this, @Nephery.
This has been fixed in 6.2.x
and main
and backported to 6.1.x
for inclusion in the upcoming 6.1.7 and 6.2.2 releases.
Comment From: Nephery
I confirmed that the fix works in the latest 6.1.17
snapshot. Thanks for the quick fix. 🙂
Comment From: sbrannen
I confirmed that the fix works in the latest
6.1.17
snapshot.
Great! Thanks for trying it out and confirming it works.
Thanks for the quick fix. 🙂
You're welcome.