Skip to content

Commit

Permalink
Merge branch '6.1.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrannen committed Jul 10, 2024
2 parents 8cfdaaa + 83ca2c0 commit 4de2aad
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -220,18 +220,37 @@ else if (spelParamCount != declaredParamCount) {
}
}

// more complex case, we need to look at conversion and vararg repacking
// more complex case, we need to look at conversion and varargs repackaging
Integer varArgPosition = null;
if (isSuspectedVarargs) {
varArgPosition = declaredParamCount - 1;
}
TypeConverter converter = state.getEvaluationContext().getTypeConverter();
ReflectionHelper.convertAllMethodHandleArguments(converter, functionArgs, methodHandle, varArgPosition);

if (isSuspectedVarargs && declaredParamCount == 1) {
// we only repack the varargs if it is the ONLY argument
functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(
methodHandle.type().parameterArray(), functionArgs);
if (isSuspectedVarargs) {
if (declaredParamCount == 1) {
// We only repackage the varargs if it is the ONLY argument -- for example,
// when we are dealing with a bound MethodHandle.
functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(
methodHandle.type().parameterArray(), functionArgs);
}
else if (spelParamCount == declaredParamCount) {
// If the varargs were supplied already packaged in an array, we have to create
// a new array, add the non-varargs arguments to the beginning of that array,
// and add the unpackaged varargs arguments to the end of that array. The reason
// is that MethodHandle.invokeWithArguments(Object...) does not expect varargs
// to be packaged in an array, in contrast to how method invocation works with
// reflection.
int actualVarargsIndex = functionArgs.length - 1;
if (actualVarargsIndex >= 0 && functionArgs[actualVarargsIndex].getClass().isArray()) {
Object[] argsToUnpack = (Object[]) functionArgs[actualVarargsIndex];
Object[] newArgs = new Object[actualVarargsIndex + argsToUnpack.length];
System.arraycopy(functionArgs, 0, newArgs, 0, actualVarargsIndex);
System.arraycopy(argsToUnpack, 0, newArgs, actualVarargsIndex, argsToUnpack.length);
functionArgs = newArgs;
}
}
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,10 +355,10 @@ public static boolean convertAllMethodHandleArguments(TypeConverter converter, O
MethodHandle methodHandle, @Nullable Integer varargsPosition) throws EvaluationException {

boolean conversionOccurred = false;
MethodType methodHandleArgumentTypes = methodHandle.type();
MethodType methodHandleType = methodHandle.type();
if (varargsPosition == null) {
for (int i = 0; i < arguments.length; i++) {
Class<?> argumentClass = methodHandleArgumentTypes.parameterType(i);
Class<?> argumentClass = methodHandleType.parameterType(i);
ResolvableType resolvableType = ResolvableType.forClass(argumentClass);
TypeDescriptor targetType = new TypeDescriptor(resolvableType, argumentClass, null);

Expand All @@ -370,7 +370,7 @@ public static boolean convertAllMethodHandleArguments(TypeConverter converter, O
else {
// Convert everything up to the varargs position
for (int i = 0; i < varargsPosition; i++) {
Class<?> argumentClass = methodHandleArgumentTypes.parameterType(i);
Class<?> argumentClass = methodHandleType.parameterType(i);
ResolvableType resolvableType = ResolvableType.forClass(argumentClass);
TypeDescriptor targetType = new TypeDescriptor(resolvableType, argumentClass, null);

Expand All @@ -379,10 +379,10 @@ public static boolean convertAllMethodHandleArguments(TypeConverter converter, O
conversionOccurred |= (argument != arguments[i]);
}

Class<?> varArgClass = methodHandleArgumentTypes.lastParameterType().componentType();
Class<?> varArgClass = methodHandleType.lastParameterType().componentType();
ResolvableType varArgResolvableType = ResolvableType.forClass(varArgClass);
TypeDescriptor varArgComponentType = new TypeDescriptor(varArgResolvableType, varArgClass, null);
TypeDescriptor componentTypeDesc = varArgComponentType.getElementTypeDescriptor();
TypeDescriptor targetType = new TypeDescriptor(varArgResolvableType, varArgClass, null);
TypeDescriptor componentTypeDesc = targetType.getElementTypeDescriptor();
// TODO Determine why componentTypeDesc can be null.
// Assert.state(componentTypeDesc != null, "Component type must not be null for a varargs array");

Expand All @@ -402,7 +402,7 @@ public static boolean convertAllMethodHandleArguments(TypeConverter converter, O
// convert a String containing a comma would result in the String being split and
// repackaged in an array when it should be used as-is.
else if (componentTypeDesc != null && !sourceType.isAssignableTo(componentTypeDesc)) {
arguments[varargsPosition] = converter.convertValue(argument, sourceType, varArgComponentType);
arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType);
}
// Possible outcomes of the above if-else block:
// 1) the input argument was null, and nothing was done.
Expand All @@ -419,7 +419,7 @@ else if (componentTypeDesc != null && !sourceType.isAssignableTo(componentTypeDe
else {
for (int i = varargsPosition; i < arguments.length; i++) {
Object argument = arguments[i];
arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), varArgComponentType);
arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType);
conversionOccurred |= (argument != arguments[i]);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package org.springframework.expression.spel;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import org.springframework.expression.spel.standard.SpelExpressionParser;
Expand Down Expand Up @@ -106,22 +105,6 @@ void functionWithVarargs() {
evaluate("#varargsFunction2(9,'a',null,'b')", "9-[a, null, b]", String.class);
}

@Disabled("Disabled until bugs are reported and fixed")
@Test
void functionWithVarargsViaMethodHandle_CurrentlyFailing() {
// Calling 'public static String formatObjectVarargs(String format, Object... args)' -> String.format(format, args)

// No conversion necessary
evaluate("#formatObjectVarargs('x -> %s', new Object[]{''})", "x -> ", String.class);
evaluate("#formatObjectVarargs('x -> %s', new String[]{''})", "x -> ", String.class);
evaluate("#formatObjectVarargs('x -> %s', new Object[]{' '})", "x -> ", String.class);
evaluate("#formatObjectVarargs('x -> %s', new String[]{' '})", "x -> ", String.class);
evaluate("#formatObjectVarargs('x -> %s', new Object[]{'a'})", "x -> a", String.class);
evaluate("#formatObjectVarargs('x -> %s', new String[]{'a'})", "x -> a", String.class);
evaluate("#formatObjectVarargs('x -> %s %s %s', new Object[]{'a', 'b', 'c'})", "x -> a b c", String.class);
evaluate("#formatObjectVarargs('x -> %s %s %s', new String[]{'a', 'b', 'c'})", "x -> a b c", String.class);
}

@Test // gh-33013
void functionWithVarargsViaMethodHandle() {
// Calling 'public static String formatObjectVarargs(String format, Object... args)' -> String.format(format, args)
Expand All @@ -138,6 +121,14 @@ void functionWithVarargsViaMethodHandle() {
evaluate("#formatObjectVarargs('x -> %s', ' ')", "x -> ", String.class);
evaluate("#formatObjectVarargs('x -> %s', 'a')", "x -> a", String.class);
evaluate("#formatObjectVarargs('x -> %s %s %s', 'a', 'b', 'c')", "x -> a b c", String.class);
evaluate("#formatObjectVarargs('x -> %s', new Object[]{''})", "x -> ", String.class);
evaluate("#formatObjectVarargs('x -> %s', new String[]{''})", "x -> ", String.class);
evaluate("#formatObjectVarargs('x -> %s', new Object[]{' '})", "x -> ", String.class);
evaluate("#formatObjectVarargs('x -> %s', new String[]{' '})", "x -> ", String.class);
evaluate("#formatObjectVarargs('x -> %s', new Object[]{'a'})", "x -> a", String.class);
evaluate("#formatObjectVarargs('x -> %s', new String[]{'a'})", "x -> a", String.class);
evaluate("#formatObjectVarargs('x -> %s %s %s', new Object[]{'a', 'b', 'c'})", "x -> a b c", String.class);
evaluate("#formatObjectVarargs('x -> %s %s %s', new String[]{'a', 'b', 'c'})", "x -> a b c", String.class);

// Conversion necessary
evaluate("#add('2', 5.0)", 7, Integer.class);
Expand Down

0 comments on commit 4de2aad

Please sign in to comment.