This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push:
new 553a047 LANG-1433: MethodUtils will throw a NPE if invokeMethod() is
called for a var-args method (#407)
553a047 is described below
commit 553a0474ea11a9b61a8de618a3512f29f5a17368
Author: Christian Franzen <[email protected]>
AuthorDate: Mon Feb 17 16:29:34 2020 +0100
LANG-1433: MethodUtils will throw a NPE if invokeMethod() is called for a
var-args method (#407)
* LANG-1433: MethodUtils will throw a NPE if invokeMethod() is called for a
var-args method with null parameter value
* LANG-1433: Result of invokeMethod() is not deterministic for overloaded
methods that can not be uniquly resolved from parameter types
* LANG-1433: Fixed checkstyle errors
---
.../apache/commons/lang3/reflect/MemberUtils.java | 5 ++-
.../apache/commons/lang3/reflect/MethodUtils.java | 47 +++++++++++++++-------
.../commons/lang3/reflect/MethodUtilsTest.java | 16 ++++++++
3 files changed, 53 insertions(+), 15 deletions(-)
diff --git a/src/main/java/org/apache/commons/lang3/reflect/MemberUtils.java
b/src/main/java/org/apache/commons/lang3/reflect/MemberUtils.java
index d7e2bd2..73fa2f1 100644
--- a/src/main/java/org/apache/commons/lang3/reflect/MemberUtils.java
+++ b/src/main/java/org/apache/commons/lang3/reflect/MemberUtils.java
@@ -162,7 +162,7 @@ abstract class MemberUtils {
// When isVarArgs is true, srcArgs and dstArgs may differ in
length.
// There are two special cases to consider:
final boolean noVarArgsPassed = srcArgs.length < destArgs.length;
- final boolean explicitArrayForVarags = srcArgs.length ==
destArgs.length && srcArgs[srcArgs.length-1].isArray();
+ final boolean explicitArrayForVarags = srcArgs.length ==
destArgs.length && srcArgs[srcArgs.length-1] != null &&
srcArgs[srcArgs.length-1].isArray();
final float varArgsCost = 0.001f;
final Class<?> destClass =
destArgs[destArgs.length-1].getComponentType();
@@ -227,6 +227,9 @@ abstract class MemberUtils {
* @return The cost of promoting the primitive
*/
private static float getPrimitivePromotionCost(final Class<?> srcClass,
final Class<?> destClass) {
+ if (srcClass == null) {
+ return 1.5f;
+ }
float cost = 0.0f;
Class<?> cls = srcClass;
if (!cls.isPrimitive()) {
diff --git a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java
b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java
index 8ebc790..c014450 100644
--- a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java
+++ b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java
@@ -25,6 +25,8 @@ import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
@@ -59,6 +61,13 @@ import org.apache.commons.lang3.Validate;
*/
public class MethodUtils {
+ private static final Comparator<Method> METHOD_BY_SIGNATURE = new
Comparator<Method>() {
+ @Override
+ public int compare(final Method m1, final Method m2) {
+ return m1.toString ().compareTo (m2.toString ());
+ }
+ };
+
/**
* <p>{@link MethodUtils} instances should NOT be constructed in standard
programming.
* Instead, the class should be used as
@@ -462,8 +471,8 @@ public class MethodUtils {
* @since 3.5
*/
static Object[] getVarArgs(final Object[] args, final Class<?>[]
methodParameterTypes) {
- if (args.length == methodParameterTypes.length
- && args[args.length -
1].getClass().equals(methodParameterTypes[methodParameterTypes.length - 1])) {
+ if (args.length == methodParameterTypes.length && (args[args.length -
1] == null ||
+ args[args.length -
1].getClass().equals(methodParameterTypes[methodParameterTypes.length - 1]))) {
// The args array is already in the canonical form for the method.
return args;
}
@@ -679,20 +688,28 @@ public class MethodUtils {
} catch (final NoSuchMethodException e) { // NOPMD - Swallow the
exception
}
// search through all methods
- Method bestMatch = null;
final Method[] methods = cls.getMethods();
+ final List<Method> matchingMethods = new ArrayList<>();
for (final Method method : methods) {
// compare name and parameters
if (method.getName().equals(methodName) &&
MemberUtils.isMatchingMethod(method, parameterTypes)) {
- // get accessible version of method
- final Method accessibleMethod = getAccessibleMethod(method);
- if (accessibleMethod != null && (bestMatch == null ||
MemberUtils.compareMethodFit(
- accessibleMethod,
- bestMatch,
- parameterTypes) < 0)) {
- bestMatch = accessibleMethod;
- }
+ matchingMethods.add (method);
+ }
+ }
+
+ // Sort methods by signature to force deterministic result
+ Collections.sort (matchingMethods, METHOD_BY_SIGNATURE);
+
+ Method bestMatch = null;
+ for (final Method method : matchingMethods) {
+ // get accessible version of method
+ final Method accessibleMethod = getAccessibleMethod(method);
+ if (accessibleMethod != null && (bestMatch == null ||
MemberUtils.compareMethodFit(
+ accessibleMethod,
+ bestMatch,
+ parameterTypes) < 0)) {
+ bestMatch = accessibleMethod;
}
}
if (bestMatch != null) {
@@ -703,10 +720,12 @@ public class MethodUtils {
final Class<?>[] methodParameterTypes =
bestMatch.getParameterTypes();
final Class<?> methodParameterComponentType =
methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
final String methodParameterComponentTypeName =
ClassUtils.primitiveToWrapper(methodParameterComponentType).getName();
- final String parameterTypeName =
parameterTypes[parameterTypes.length - 1].getName();
- final String parameterTypeSuperClassName =
parameterTypes[parameterTypes.length - 1].getSuperclass().getName();
- if (!methodParameterComponentTypeName.equals(parameterTypeName)
+ final Class<?> lastParameterType =
parameterTypes[parameterTypes.length - 1];
+ final String parameterTypeName = (lastParameterType==null) ? null
: lastParameterType.getName();
+ final String parameterTypeSuperClassName =
(lastParameterType==null) ? null : lastParameterType.getSuperclass().getName();
+
+ if (parameterTypeName!= null && parameterTypeSuperClassName !=
null && !methodParameterComponentTypeName.equals(parameterTypeName)
&&
!methodParameterComponentTypeName.equals(parameterTypeSuperClassName)) {
return null;
}
diff --git
a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java
b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java
index 9df5a14..168bc2a 100644
--- a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java
@@ -447,6 +447,22 @@ public class MethodUtilsTest {
}
@Test
+ public void testInvokeMethod_VarArgsWithNullValues() throws Exception {
+ assertEquals("String...", MethodUtils.invokeMethod(testBean,
"varOverload",
+ "a", null, "c"));
+ assertEquals("String...", MethodUtils.invokeMethod(testBean,
"varOverload",
+ "a", "b",
null));
+ }
+
+ @Test
+ public void testInvokeMethod_VarArgsNotUniqueResolvable() throws Exception
{
+ assertEquals("Boolean...", MethodUtils.invokeMethod(testBean,
"varOverload",
+ new Object[] {null}));
+ assertEquals("Object...", MethodUtils.invokeMethod(testBean,
"varOverload",
+ (Object[]) null));
+ }
+
+ @Test
public void testInvokeExactMethod() throws Exception {
assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo",
(Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));