7044892: JSR 292: API entry points sometimes throw the wrong exceptions or doesn't throw the expected one
authorjrose
Tue, 17 May 2011 19:48:19 -0700
changeset 9731 d0f7a3e441c4
parent 9730 e4b334d47f4b
child 9732 2424049d3308
child 9752 88ab34b6da6d
7044892: JSR 292: API entry points sometimes throw the wrong exceptions or doesn't throw the expected one Summary: point-fixes for 7038847, 7038860, 7042656, 7042829, 7041853, and several other reports Reviewed-by: never, kvn
jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java
jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java
jdk/src/share/classes/java/lang/invoke/FilterGeneric.java
jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java
jdk/src/share/classes/java/lang/invoke/FromGeneric.java
jdk/src/share/classes/java/lang/invoke/MethodHandle.java
jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java
jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java
jdk/src/share/classes/java/lang/invoke/MethodHandles.java
jdk/src/share/classes/java/lang/invoke/MethodType.java
jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java
jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java
jdk/src/share/classes/java/lang/invoke/ToGeneric.java
jdk/test/java/lang/invoke/MethodHandlesTest.java
--- a/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java	Tue May 17 19:48:19 2011 -0700
@@ -546,6 +546,10 @@
     }
 
     static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
+        MethodType type = target.type();
+        int last = type.parameterCount() - 1;
+        if (type.parameterType(last) != arrayType)
+            target = target.asType(type.changeParameterType(last, arrayType));
         return new AsVarargsCollector(target, arrayType);
     }
 
@@ -1144,7 +1148,7 @@
     }
 
     @Override
-    public String toString() {
+    String debugString() {
         return getNameString(nonAdapter((MethodHandle)vmtarget), this);
     }
 
--- a/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java	Tue May 17 19:48:19 2011 -0700
@@ -155,7 +155,7 @@
     }
 
     @Override
-    public String toString() {
+    String debugString() {
         return addTypeString(baseName(), this);
     }
 
--- a/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java	Tue May 17 19:48:19 2011 -0700
@@ -234,7 +234,7 @@
         protected final MethodHandle target; // ultimate target
 
         @Override
-        public String toString() {
+        String debugString() {
             return addTypeString(target, this);
         }
 
--- a/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java	Tue May 17 19:48:19 2011 -0700
@@ -41,7 +41,7 @@
     protected final MethodHandle target;  // Object -> Object
 
     @Override
-    public String toString() {
+    String debugString() {
         return target.toString();
     }
 
--- a/jdk/src/share/classes/java/lang/invoke/FromGeneric.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FromGeneric.java	Tue May 17 19:48:19 2011 -0700
@@ -260,7 +260,7 @@
         protected final MethodHandle target;   // (any**N) => R
 
         @Override
-        public String toString() {
+        String debugString() {
             return addTypeString(target, this);
         }
 
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java	Tue May 17 19:48:19 2011 -0700
@@ -26,6 +26,7 @@
 package java.lang.invoke;
 
 
+import java.util.ArrayList;
 import sun.invoke.util.ValueConversions;
 import static java.lang.invoke.MethodHandleStatics.*;
 
@@ -750,11 +751,46 @@
      * @see #asCollector
      */
     public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
-        Class<?> arrayElement = arrayType.getComponentType();
-        if (arrayElement == null)  throw newIllegalArgumentException("not an array type");
+        asSpreaderChecks(arrayType, arrayLength);
+        return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
+    }
+
+    private void asSpreaderChecks(Class<?> arrayType, int arrayLength) {
+        spreadArrayChecks(arrayType, arrayLength);
         int nargs = type().parameterCount();
         if (nargs < arrayLength)  throw newIllegalArgumentException("bad spread array length");
-        return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
+        if (arrayType != Object[].class && arrayLength != 0) {
+            boolean sawProblem = false;
+            Class<?> arrayElement = arrayType.getComponentType();
+            for (int i = nargs - arrayLength; i < nargs; i++) {
+                if (!MethodType.canConvert(arrayElement, type().parameterType(i))) {
+                    sawProblem = true;
+                    break;
+                }
+            }
+            if (sawProblem) {
+                ArrayList<Class<?>> ptypes = new ArrayList<Class<?>>(type().parameterList());
+                for (int i = nargs - arrayLength; i < nargs; i++) {
+                    ptypes.set(i, arrayElement);
+                }
+                // elicit an error:
+                this.asType(MethodType.methodType(type().returnType(), ptypes));
+            }
+        }
+    }
+
+    private void spreadArrayChecks(Class<?> arrayType, int arrayLength) {
+        Class<?> arrayElement = arrayType.getComponentType();
+        if (arrayElement == null)
+            throw newIllegalArgumentException("not an array type", arrayType);
+        if ((arrayLength & 0x7F) != arrayLength) {
+            if ((arrayLength & 0xFF) != arrayLength)
+                throw newIllegalArgumentException("array length is not legal", arrayLength);
+            assert(arrayLength >= 128);
+            if (arrayElement == long.class ||
+                arrayElement == double.class)
+                throw newIllegalArgumentException("array length is not legal for long[] or double[]", arrayLength);
+        }
     }
 
     /**
@@ -802,10 +838,8 @@
         return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
     }
 
-    private  void asCollectorChecks(Class<?> arrayType, int arrayLength) {
-        Class<?> arrayElement = arrayType.getComponentType();
-        if (arrayElement == null)
-            throw newIllegalArgumentException("not an array type", arrayType);
+    private void asCollectorChecks(Class<?> arrayType, int arrayLength) {
+        spreadArrayChecks(arrayType, arrayLength);
         int nargs = type().parameterCount();
         if (nargs == 0 || !type().parameterType(nargs-1).isAssignableFrom(arrayType))
             throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
@@ -965,8 +999,8 @@
      */
     public MethodHandle asVarargsCollector(Class<?> arrayType) {
         Class<?> arrayElement = arrayType.getComponentType();
-        if (arrayElement == null)  throw newIllegalArgumentException("not an array type");
-        return MethodHandles.asVarargsCollector(this, arrayType);
+        asCollectorChecks(arrayType, 0);
+        return AdapterMethodHandle.makeVarargsCollector(this, arrayType);
     }
 
     /**
@@ -1043,6 +1077,12 @@
      */
     @Override
     public String toString() {
+        if (DEBUG_METHOD_HANDLE_NAMES)  return debugString();
+        return "MethodHandle"+type;
+    }
+
+    /*non-public*/
+    String debugString() {
         return getNameString(this);
     }
 }
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Tue May 17 19:48:19 2011 -0700
@@ -163,7 +163,7 @@
             }
         }
         @Override
-        public String toString() {
+        String debugString() {
             return addTypeString(allocateClass.getSimpleName(), this);
         }
         @SuppressWarnings("unchecked")
@@ -307,7 +307,7 @@
             this.base = staticBase(field);
         }
         @Override
-        public String toString() { return addTypeString(name, this); }
+        String debugString() { return addTypeString(name, this); }
 
         int getFieldI(C obj) { return unsafe.getInt(obj, offset); }
         void setFieldI(C obj, int x) { unsafe.putInt(obj, offset, x); }
@@ -338,8 +338,9 @@
             try {
                 // FIXME:  Should not have to create 'f' to get this value.
                 f = c.getDeclaredField(field.getName());
+                // Note:  Previous line might invalidly throw SecurityException (7042829)
                 return unsafe.staticFieldBase(f);
-            } catch (Exception ee) {
+            } catch (NoSuchFieldException ee) {
                 throw uncaughtException(ee);
             }
         }
@@ -936,7 +937,7 @@
             }
         }
         @Override
-        public String toString() {
+        String debugString() {
             return addTypeString(target, this);
         }
         private Object invoke_V(Object... av) throws Throwable {
@@ -1081,7 +1082,7 @@
             this.catcher = catcher;
         }
         @Override
-        public String toString() {
+        String debugString() {
             return addTypeString(target, this);
         }
         private Object invoke_V(Object... av) throws Throwable {
@@ -1248,8 +1249,4 @@
     static MethodHandle getBootstrap(Class<?> callerClass) {
         return MethodHandleNatives.getBootstrap(callerClass);
     }
-
-    static MethodHandle asVarargsCollector(MethodHandle target, Class<?> arrayType) {
-        return AdapterMethodHandle.makeVarargsCollector(target, arrayType);
-    }
 }
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java	Tue May 17 19:48:19 2011 -0700
@@ -35,6 +35,8 @@
 
     private MethodHandleStatics() { }  // do not instantiate
 
+    static final boolean DEBUG_METHOD_HANDLE_NAMES = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
+
     /*non-public*/ static String getNameString(MethodHandle target, MethodType type) {
         if (type == null)
             type = target.type();
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java	Tue May 17 19:48:19 2011 -0700
@@ -1065,6 +1065,7 @@
             if (!method.isProtected() || method.isStatic()
                 || allowedModes == TRUSTED
                 || method.getDeclaringClass() == lookupClass()
+                || VerifyAccess.isSamePackage(method.getDeclaringClass(), lookupClass())
                 || (ALLOW_NESTMATE_ACCESS &&
                     VerifyAccess.isSamePackageMember(method.getDeclaringClass(), lookupClass())))
                 return mh;
@@ -2383,9 +2384,4 @@
         }
         return null;
     }
-
-    /*non-public*/
-    static MethodHandle asVarargsCollector(MethodHandle target, Class<?> arrayType) {
-        return MethodHandleImpl.asVarargsCollector(target, arrayType);
-    }
 }
--- a/jdk/src/share/classes/java/lang/invoke/MethodType.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodType.java	Tue May 17 19:48:19 2011 -0700
@@ -163,7 +163,13 @@
     public static
     MethodType methodType(Class<?> rtype, List<Class<?>> ptypes) {
         boolean notrust = false;  // random List impl. could return evil ptypes array
-        return makeImpl(rtype, ptypes.toArray(NO_PTYPES), notrust);
+        return makeImpl(rtype, listToArray(ptypes), notrust);
+    }
+
+    private static Class<?>[] listToArray(List<Class<?>> ptypes) {
+        // sanity check the size before the toArray call, since size might be huge
+        checkSlotCount(ptypes.size());
+        return ptypes.toArray(NO_PTYPES);
     }
 
     /**
@@ -228,7 +234,7 @@
      */
     /*trusted*/ static
     MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
-        if (ptypes == null || ptypes.length == 0) {
+        if (ptypes.length == 0) {
             ptypes = NO_PTYPES; trusted = true;
         }
         MethodType mt1 = new MethodType(rtype, ptypes);
@@ -372,7 +378,7 @@
      * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
      */
     public MethodType insertParameterTypes(int num, List<Class<?>> ptypesToInsert) {
-        return insertParameterTypes(num, ptypesToInsert.toArray(NO_PTYPES));
+        return insertParameterTypes(num, listToArray(ptypesToInsert));
     }
 
     /**
@@ -641,7 +647,8 @@
         }
         return true;
     }
-    private static boolean canConvert(Class<?> src, Class<?> dst) {
+    /*non-public*/
+    static boolean canConvert(Class<?> src, Class<?> dst) {
         if (src == dst || dst == void.class)  return true;
         if (src.isPrimitive() && dst.isPrimitive()) {
         if (!Wrapper.forPrimitiveType(dst)
@@ -739,9 +746,14 @@
     public static MethodType fromMethodDescriptorString(String descriptor, ClassLoader loader)
         throws IllegalArgumentException, TypeNotPresentException
     {
+        if (!descriptor.startsWith("(") ||  // also generates NPE if needed
+            descriptor.indexOf(')') < 0 ||
+            descriptor.indexOf('.') >= 0)
+            throw new IllegalArgumentException("not a method descriptor: "+descriptor);
         List<Class<?>> types = BytecodeDescriptor.parseMethod(descriptor, loader);
         Class<?> rtype = types.remove(types.size() - 1);
-        Class<?>[] ptypes = types.toArray(NO_PTYPES);
+        checkSlotCount(types.size());
+        Class<?>[] ptypes = listToArray(types);
         return makeImpl(rtype, ptypes, true);
     }
 
--- a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java	Tue May 17 19:48:19 2011 -0700
@@ -448,6 +448,8 @@
         Class<?>[] cs = null;
         for (int imax = ts.length, i = 0; i < imax; i++) {
             Class<?> c = canonicalize(ts[i], how);
+            if (c == void.class)
+                c = null;  // a Void parameter was unwrapped to void; ignore
             if (c != null) {
                 if (cs == null)
                     cs = ts.clone();
--- a/jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java	Tue May 17 19:48:19 2011 -0700
@@ -126,7 +126,7 @@
         return spreadGen;
     }
 
-    public String toString() {
+    String debugString() {
         return getClass().getSimpleName()+targetType+"["+spreadCount+"]";
     }
 
@@ -224,7 +224,7 @@
         protected final MethodHandle target;   // (any**N) => R
 
         @Override
-        public String toString() {
+        String debugString() {
             return addTypeString(target, this);
         }
 
--- a/jdk/src/share/classes/java/lang/invoke/ToGeneric.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/ToGeneric.java	Tue May 17 19:48:19 2011 -0700
@@ -258,7 +258,7 @@
         return toGen;
     }
 
-    public String toString() {
+    String debugString() {
         return "ToGeneric"+entryType
                 +(primsAtEndOrder!=null?"[reorder]":"");
     }
@@ -340,7 +340,7 @@
         protected final MethodHandle convert;  // Object -> R
 
         @Override
-        public String toString() {
+        String debugString() {
             return target == null ? "prototype:"+convert : addTypeString(target, this);
         }
 
--- a/jdk/test/java/lang/invoke/MethodHandlesTest.java	Tue May 17 19:48:14 2011 -0700
+++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java	Tue May 17 19:48:19 2011 -0700
@@ -505,8 +505,15 @@
             System.out.print(':');
     }
 
+    static final boolean DEBUG_METHOD_HANDLE_NAMES = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
+
     // rough check of name string
-    static void assertNameStringContains(Object x, String s) {
+    static void assertNameStringContains(MethodHandle x, String s) {
+        if (!DEBUG_METHOD_HANDLE_NAMES) {
+            // ignore s
+            assertEquals("MethodHandle"+x.type(), x.toString());
+            return;
+        }
         if (x.toString().contains(s))  return;
         assertEquals(s, x);
     }