8280377: MethodHandleProxies does not correctly invoke default methods with varags

Reviewed-by: alanb
This commit is contained in:
Mandy Chung 2022-01-26 01:24:15 +00:00
parent 2eab86b513
commit a183bfb436
12 changed files with 295 additions and 66 deletions

View file

@ -28,7 +28,6 @@ package java.lang.reflect;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import java.lang.invoke.MethodHandle;
import java.util.Objects;
/**
@ -262,33 +261,6 @@ public interface InvocationHandler {
throws Throwable {
Objects.requireNonNull(proxy);
Objects.requireNonNull(method);
// verify that the object is actually a proxy instance
if (!Proxy.isProxyClass(proxy.getClass())) {
throw new IllegalArgumentException("'proxy' is not a proxy instance");
}
if (!method.isDefault()) {
throw new IllegalArgumentException("\"" + method + "\" is not a default method");
}
@SuppressWarnings("unchecked")
Class<? extends Proxy> proxyClass = (Class<? extends Proxy>)proxy.getClass();
Class<?> intf = method.getDeclaringClass();
// access check on the default method
method.checkAccess(Reflection.getCallerClass(), intf, proxyClass, method.getModifiers());
MethodHandle mh = Proxy.defaultMethodHandle(proxyClass, method);
// invoke the super method
try {
// the args array can be null if the number of formal parameters required by
// the method is zero (consistent with Method::invoke)
Object[] params = args != null ? args : Proxy.EMPTY_ARGS;
return mh.invokeExact(proxy, params);
} catch (ClassCastException | NullPointerException e) {
throw new IllegalArgumentException(e.getMessage(), e);
} catch (Proxy.InvocationException e) {
// unwrap and throw the exception thrown by the default method
throw e.getCause();
}
return Proxy.invokeDefault(proxy, method, args, Reflection.getCallerClass());
}
}

View file

@ -1314,6 +1314,46 @@ public class Proxy implements java.io.Serializable {
});
}
/*
* Invoke the default method of the given proxy with an explicit caller class.
*
* @throws IllegalAccessException if the proxy interface is inaccessible to the caller
* if caller is non-null
*/
static Object invokeDefault(Object proxy, Method method, Object[] args, Class<?> caller)
throws Throwable {
// verify that the object is actually a proxy instance
if (!Proxy.isProxyClass(proxy.getClass())) {
throw new IllegalArgumentException("'proxy' is not a proxy instance");
}
if (!method.isDefault()) {
throw new IllegalArgumentException("\"" + method + "\" is not a default method");
}
@SuppressWarnings("unchecked")
Class<? extends Proxy> proxyClass = (Class<? extends Proxy>)proxy.getClass();
// skip access check if caller is null
if (caller != null) {
Class<?> intf = method.getDeclaringClass();
// access check on the default method
method.checkAccess(caller, intf, proxyClass, method.getModifiers());
}
MethodHandle mh = Proxy.defaultMethodHandle(proxyClass, method);
// invoke the super method
try {
// the args array can be null if the number of formal parameters required by
// the method is zero (consistent with Method::invoke)
Object[] params = args != null ? args : Proxy.EMPTY_ARGS;
return mh.invokeExact(proxy, params);
} catch (ClassCastException | NullPointerException e) {
throw new IllegalArgumentException(e.getMessage(), e);
} catch (Proxy.InvocationException e) {
// unwrap and throw the exception thrown by the default method
throw e.getCause();
}
}
/**
* Internal exception type to wrap the exception thrown by the default method
* so that it can distinguish CCE and NPE thrown due to the arguments

View file

@ -127,4 +127,9 @@ class ReflectAccess implements jdk.internal.access.JavaLangReflectAccess {
{
return ctor.newInstanceWithCaller(args, true, caller);
}
public Object invokeDefault(Object proxy, Method method, Object[] args, Class<?> caller)
throws Throwable {
return Proxy.invokeDefault(proxy, method, args, caller);
}
}