7054590: (JSR-292) MethodHandleProxies.asInterfaceInstance() accepts private/protected nested interfaces
Summary: fix non-compliant logic in MethodHandleProxies, fix invalid private classes in MethodHandlesTest
Reviewed-by: twisti, never
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleProxies.java Fri Jun 03 11:20:20 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleProxies.java Tue Jun 14 22:47:12 2011 -0700
@@ -27,6 +27,7 @@
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 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 @@
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 @@
}
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()]);
}
}
--- a/jdk/test/java/lang/invoke/MethodHandlesTest.java Fri Jun 03 11:20:20 2011 -0700
+++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java Tue Jun 14 22:47:12 2011 -0700
@@ -2234,7 +2234,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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