Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SpEL fails to invoke MethodHandle function reference accepting only varargs #34109

Closed
Nephery opened this issue Dec 17, 2024 · 3 comments
Closed
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Milestone

Comments

@Nephery
Copy link

Nephery commented Dec 17, 2024

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);
	}
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 17, 2024
@Nephery Nephery changed the title SpEL Cannot invoke function references which only have varargs parameters SpEL fails to invoke function references which only have varargs parameters Dec 17, 2024
@sbrannen sbrannen added the in: core Issues in core modules (aop, beans, core, context, expression) label Dec 18, 2024
@sbrannen sbrannen self-assigned this Dec 18, 2024
@sbrannen sbrannen added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Dec 18, 2024
@sbrannen sbrannen changed the title SpEL fails to invoke function references which only have varargs parameters SpEL fails to invoke MethodHandle function reference accepting only varargs Dec 18, 2024
@sbrannen sbrannen added this to the 6.2.2 milestone Dec 18, 2024
@sbrannen sbrannen added the for: backport-to-6.1.x Marks an issue as a candidate for backport to 6.1.x label Dec 18, 2024
@github-actions github-actions bot added status: backported An issue that has been backported to maintenance branches and removed for: backport-to-6.1.x Marks an issue as a candidate for backport to 6.1.x labels Dec 18, 2024
sbrannen added a commit that referenced this issue Dec 18, 2024
Prior to this commit, if a MethodHandle was registered as a custom
function in the Spring Expression Language (SpEL) for a static method
that accepted only a variable argument list (for example,
`static String func(String... args)`), attempting to invoke the
registered function within a SpEL expression resulted in a
ClassCastException because the varargs array was unnecessarily wrapped
in an Object[].

This commit modifies the logic in FunctionReference's internal
executeFunctionViaMethodHandle() method to address that.

Closes gh-34109
@sbrannen
Copy link
Member

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.

@Nephery
Copy link
Author

Nephery commented Dec 18, 2024

I confirmed that the fix works in the latest 6.1.17 snapshot. Thanks for the quick fix. 🙂

@sbrannen
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants