7054590: (JSR-292) MethodHandleProxies.asInterfaceInstance() accepts private/protected nested interfaces
authorjrose
Tue, 14 Jun 2011 22:47:12 -0700
changeset 10078 944d876457df
parent 9782 973c21557e1a
child 10079 0ed5b8d18ae4
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
jdk/src/share/classes/java/lang/invoke/MethodHandleProxies.java
jdk/test/java/lang/invoke/MethodHandlesTest.java
--- 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