7054590: (JSR-292) MethodHandleProxies.asInterfaceInstance() accepts private/protected nested interfaces
Fix non-compliant logic in MethodHandleProxies, fix invalid private classes in MethodHandlesTest Reviewed-by: twisti, never
This commit is contained in:
parent
ad33af1a1c
commit
1f3aab409b
@ -27,6 +27,7 @@ package java.lang.invoke;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import sun.invoke.WrapperInstance;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This class consists exclusively of static methods that help adapt
|
||||
@ -134,14 +135,19 @@ public class MethodHandleProxies {
|
||||
//
|
||||
public static
|
||||
<T> T asInterfaceInstance(final Class<T> intfc, final MethodHandle target) {
|
||||
// POC implementation only; violates the above contract several ways
|
||||
final Method sm = getSingleMethod(intfc);
|
||||
if (sm == null)
|
||||
if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers()))
|
||||
throw new IllegalArgumentException("not a public interface: "+intfc.getName());
|
||||
final Method[] methods = getSingleNameMethods(intfc);
|
||||
if (methods == null)
|
||||
throw new IllegalArgumentException("not a single-method interface: "+intfc.getName());
|
||||
MethodType smMT = MethodType.methodType(sm.getReturnType(), sm.getParameterTypes());
|
||||
MethodHandle checkTarget = target.asType(smMT); // make throw WMT
|
||||
checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class));
|
||||
final MethodHandle vaTarget = checkTarget.asSpreader(Object[].class, smMT.parameterCount());
|
||||
final MethodHandle[] vaTargets = new MethodHandle[methods.length];
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
Method sm = methods[i];
|
||||
MethodType smMT = MethodType.methodType(sm.getReturnType(), sm.getParameterTypes());
|
||||
MethodHandle checkTarget = target.asType(smMT); // make throw WMT
|
||||
checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class));
|
||||
vaTargets[i] = checkTarget.asSpreader(Object[].class, smMT.parameterCount());
|
||||
}
|
||||
return intfc.cast(Proxy.newProxyInstance(
|
||||
intfc.getClassLoader(),
|
||||
new Class[]{ intfc, WrapperInstance.class },
|
||||
@ -152,13 +158,15 @@ public class MethodHandleProxies {
|
||||
throw new AssertionError();
|
||||
}
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (method.equals(methods[i]))
|
||||
return vaTargets[i].invokeExact(args);
|
||||
}
|
||||
if (method.getDeclaringClass() == WrapperInstance.class)
|
||||
return getArg(method.getName());
|
||||
if (method.equals(sm))
|
||||
return vaTarget.invokeExact(args);
|
||||
if (isObjectMethod(method))
|
||||
return callObjectMethod(this, method, args);
|
||||
throw new InternalError();
|
||||
throw new InternalError("bad proxy method: "+method);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@ -241,17 +249,20 @@ public class MethodHandleProxies {
|
||||
}
|
||||
|
||||
private static
|
||||
Method getSingleMethod(Class<?> intfc) {
|
||||
if (!intfc.isInterface()) return null;
|
||||
Method sm = null;
|
||||
Method[] getSingleNameMethods(Class<?> intfc) {
|
||||
ArrayList<Method> methods = new ArrayList<Method>();
|
||||
String uniqueName = null;
|
||||
for (Method m : intfc.getMethods()) {
|
||||
int mod = m.getModifiers();
|
||||
if (Modifier.isAbstract(mod)) {
|
||||
if (sm != null && !isObjectMethod(sm))
|
||||
return null; // too many abstract methods
|
||||
sm = m;
|
||||
}
|
||||
if (isObjectMethod(m)) continue;
|
||||
if (!Modifier.isAbstract(m.getModifiers())) continue;
|
||||
String mname = m.getName();
|
||||
if (uniqueName == null)
|
||||
uniqueName = mname;
|
||||
else if (!uniqueName.equals(mname))
|
||||
return null; // too many abstract methods
|
||||
methods.add(m);
|
||||
}
|
||||
return sm;
|
||||
if (uniqueName == null) return null;
|
||||
return methods.toArray(new Method[methods.size()]);
|
||||
}
|
||||
}
|
||||
|
@ -2234,7 +2234,7 @@ public class MethodHandlesTest {
|
||||
static void runForRunnable() {
|
||||
called("runForRunnable");
|
||||
}
|
||||
private interface Fooable {
|
||||
public interface Fooable {
|
||||
Object foo(Fooable x, Object y);
|
||||
// this is for randomArg:
|
||||
public class Impl implements Fooable {
|
||||
@ -2249,9 +2249,9 @@ public class MethodHandlesTest {
|
||||
static Object fooForFooable(Fooable x, Object y) {
|
||||
return called("fooForFooable", x, y);
|
||||
}
|
||||
private static class MyCheckedException extends Exception {
|
||||
public static class MyCheckedException extends Exception {
|
||||
}
|
||||
private interface WillThrow {
|
||||
public interface WillThrow {
|
||||
void willThrow() throws MyCheckedException;
|
||||
}
|
||||
|
||||
@ -2300,8 +2300,11 @@ public class MethodHandlesTest {
|
||||
assertSame("must pass declared exception out without wrapping", ex, ex1);
|
||||
} else {
|
||||
assertNotSame("must pass undeclared checked exception with wrapping", ex, ex1);
|
||||
if (!(ex1 instanceof UndeclaredThrowableException) || ex1.getCause() != ex) {
|
||||
ex1.printStackTrace();
|
||||
}
|
||||
assertSame(ex, ex1.getCause());
|
||||
UndeclaredThrowableException utex = (UndeclaredThrowableException) ex1;
|
||||
assertSame(ex, utex.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2380,8 +2383,7 @@ class ValueConversions {
|
||||
public static MethodHandle varargsArray(int nargs) {
|
||||
if (nargs < ARRAYS.length)
|
||||
return ARRAYS[nargs];
|
||||
// else need to spin bytecode or do something else fancy
|
||||
throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs);
|
||||
return MethodHandles.identity(Object[].class).asCollector(Object[].class, nargs);
|
||||
}
|
||||
public static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
|
||||
Class<?> elemType = arrayType.getComponentType();
|
||||
@ -2463,6 +2465,12 @@ class ValueConversions {
|
||||
return lists.toArray(new MethodHandle[0]);
|
||||
}
|
||||
static final MethodHandle[] LISTS = makeLists();
|
||||
static final MethodHandle AS_LIST;
|
||||
static {
|
||||
try {
|
||||
AS_LIST = IMPL_LOOKUP.findStatic(Arrays.class, "asList", MethodType.methodType(List.class, Object[].class));
|
||||
} catch (Exception ex) { throw new RuntimeException(ex); }
|
||||
}
|
||||
|
||||
/** Return a method handle that takes the indicated number of Object
|
||||
* arguments and returns List.
|
||||
@ -2470,8 +2478,7 @@ class ValueConversions {
|
||||
public static MethodHandle varargsList(int nargs) {
|
||||
if (nargs < LISTS.length)
|
||||
return LISTS[nargs];
|
||||
// else need to spin bytecode or do something else fancy
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
return AS_LIST.asCollector(Object[].class, nargs);
|
||||
}
|
||||
}
|
||||
// This guy tests access from outside the same package member, but inside
|
||||
|
Loading…
x
Reference in New Issue
Block a user