7013730: JSR 292 reflective operations should report errors with standard exception types
authorjrose
Fri, 11 Feb 2011 01:26:32 -0800
changeset 8347 e5daa5772ffd
parent 8346 3b891698c4ec
child 8348 3b2ac15dfc16
7013730: JSR 292 reflective operations should report errors with standard exception types Summary: remove NoAccessException, replace it by ReflectiveOperationException subtypes; adjust javadoc of exceptions Reviewed-by: twisti
jdk/src/share/classes/java/dyn/CallSite.java
jdk/src/share/classes/java/dyn/Linkage.java
jdk/src/share/classes/java/dyn/MethodHandles.java
jdk/src/share/classes/java/dyn/NoAccessException.java
jdk/src/share/classes/sun/dyn/CallSiteImpl.java
jdk/src/share/classes/sun/dyn/FilterGeneric.java
jdk/src/share/classes/sun/dyn/FilterOneArgument.java
jdk/src/share/classes/sun/dyn/FromGeneric.java
jdk/src/share/classes/sun/dyn/InvokeGeneric.java
jdk/src/share/classes/sun/dyn/Invokers.java
jdk/src/share/classes/sun/dyn/MemberName.java
jdk/src/share/classes/sun/dyn/MethodHandleImpl.java
jdk/src/share/classes/sun/dyn/MethodHandleNatives.java
jdk/src/share/classes/sun/dyn/SpreadGeneric.java
jdk/src/share/classes/sun/dyn/ToGeneric.java
jdk/src/share/classes/sun/dyn/util/ValueConversions.java
jdk/src/share/classes/sun/dyn/util/VerifyAccess.java
jdk/test/java/dyn/InvokeGenericTest.java
jdk/test/java/dyn/JavaDocExamplesTest.java
jdk/test/java/dyn/MethodHandlesTest.java
--- a/jdk/src/share/classes/java/dyn/CallSite.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/CallSite.java	Fri Feb 11 01:26:32 2011 -0800
@@ -228,7 +228,7 @@
         try {
             GET_TARGET = MethodHandles.Lookup.IMPL_LOOKUP.
                 findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
-        } catch (NoAccessException ignore) {
+        } catch (ReflectiveOperationException ignore) {
             throw new InternalError();
         }
     }
--- a/jdk/src/share/classes/java/dyn/Linkage.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/Linkage.java	Fri Feb 11 01:26:32 2011 -0800
@@ -88,7 +88,7 @@
         MethodHandle bootstrapMethod;
         try {
             bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE);
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex);
         }
         MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod);
--- a/jdk/src/share/classes/java/dyn/MethodHandles.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/MethodHandles.java	Fri Feb 11 01:26:32 2011 -0800
@@ -226,9 +226,14 @@
      * the containing class is not accessible to the lookup class, or
      * because the desired class member is missing, or because the
      * desired class member is not accessible to the lookup class.
-     * It can also fail if a security manager is installed and refuses
-     * access.  In any of these cases, an exception will be
-     * thrown from the attempted lookup.
+     * In any of these cases, a {@code ReflectiveOperationException} will be
+     * thrown from the attempted lookup.  The exact class will be one of
+     * the following:
+     * <ul>
+     * <li>NoSuchMethodException &mdash; if a method is requested but does not exist
+     * <li>NoSuchFieldException &mdash; if a field is requested but does not exist
+     * <li>IllegalAccessException &mdash; if the member exists but an access check fails
+     * </ul>
      * <p>
      * In general, the conditions under which a method handle may be
      * looked up for a method {@code M} are exactly equivalent to the conditions
@@ -511,10 +516,12 @@
          * @param name the name of the method
          * @param type the type of the method
          * @return the desired method handle
-         * @exception NoAccessException if the method does not exist or access checking fails
+         * @throws NoSuchMethodException if the method does not exist
+         * @throws IllegalAccessException if access checking fails, or if the method is not {@code static}
+         * @throws NullPointerException if any argument is null
          */
         public
-        MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoAccessException {
+        MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             MemberName method = resolveOrFail(refc, name, type, true);
             checkMethod(refc, method, true);
             return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClassOrNull());
@@ -549,9 +556,11 @@
          * @param name the name of the method
          * @param type the type of the method, with the receiver argument omitted
          * @return the desired method handle
-         * @exception NoAccessException if the method does not exist or access checking fails
+         * @throws NoSuchMethodException if the method does not exist
+         * @throws IllegalAccessException if access checking fails, or if the method is {@code static}
+         * @throws NullPointerException if any argument is null
          */
-        public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoAccessException {
+        public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             MemberName method = resolveOrFail(refc, name, type, false);
             checkMethod(refc, method, false);
             MethodHandle mh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClassOrNull());
@@ -576,9 +585,11 @@
          * @param refc the class or interface from which the method is accessed
          * @param type the type of the method, with the receiver argument omitted, and a void return type
          * @return the desired method handle
-         * @exception NoAccessException if the method does not exist or access checking fails
+         * @throws NoSuchMethodException if the constructor does not exist
+         * @throws IllegalAccessException if access checking fails
+         * @throws NullPointerException if any argument is null
          */
-        public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoAccessException {
+        public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             String name = "<init>";
             MemberName ctor = resolveOrFail(refc, name, type, false, false, lookupClassOrNull());
             assert(ctor.isConstructor());
@@ -629,10 +640,12 @@
          * @param type the type of the method, with the receiver argument omitted
          * @param specialCaller the proposed calling class to perform the {@code invokespecial}
          * @return the desired method handle
-         * @exception NoAccessException if the method does not exist or access checking fails
+         * @throws NoSuchMethodException if the method does not exist
+         * @throws IllegalAccessException if access checking fails
+         * @throws NullPointerException if any argument is null
          */
         public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
-                                        Class<?> specialCaller) throws NoAccessException {
+                                        Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
             checkSpecialCaller(specialCaller);
             MemberName method = resolveOrFail(refc, name, type, false, false, specialCaller);
             checkMethod(refc, method, false);
@@ -651,9 +664,11 @@
          * @param name the field's name
          * @param type the field's type
          * @return a method handle which can load values from the field
-         * @exception NoAccessException if access checking fails
+         * @throws NoSuchFieldException if the field does not exist
+         * @throws IllegalAccessException if access checking fails, or if the field is {@code static}
+         * @throws NullPointerException if any argument is null
          */
-        public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoAccessException {
+        public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
             return makeAccessor(refc, name, type, false, false);
         }
 
@@ -668,9 +683,11 @@
          * @param name the field's name
          * @param type the field's type
          * @return a method handle which can store values into the field
-         * @exception NoAccessException if access checking fails
+         * @throws NoSuchFieldException if the field does not exist
+         * @throws IllegalAccessException if access checking fails, or if the field is {@code static}
+         * @throws NullPointerException if any argument is null
          */
-        public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoAccessException {
+        public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
             return makeAccessor(refc, name, type, false, true);
         }
 
@@ -684,9 +701,11 @@
          * @param name the field's name
          * @param type the field's type
          * @return a method handle which can load values from the field
-         * @exception NoAccessException if access checking fails
+         * @throws NoSuchFieldException if the field does not exist
+         * @throws IllegalAccessException if access checking fails, or if the field is not {@code static}
+         * @throws NullPointerException if any argument is null
          */
-        public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoAccessException {
+        public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
             return makeAccessor(refc, name, type, true, false);
         }
 
@@ -700,9 +719,11 @@
          * @param name the field's name
          * @param type the field's type
          * @return a method handle which can store values into the field
-         * @exception NoAccessException if access checking fails
+         * @throws NoSuchFieldException if the field does not exist
+         * @throws IllegalAccessException if access checking fails, or if the field is not {@code static}
+         * @throws NullPointerException if any argument is null
          */
-        public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoAccessException {
+        public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
             return makeAccessor(refc, name, type, true, true);
         }
 
@@ -741,16 +762,18 @@
          * @param name the name of the method
          * @param type the type of the method, with the receiver argument omitted
          * @return the desired method handle
-         * @exception NoAccessException if the method does not exist or access checking fails
+         * @throws NoSuchMethodException if the method does not exist
+         * @throws IllegalAccessException if access checking fails
+         * @throws NullPointerException if any argument is null
          */
-        public MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException {
+        public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             Class<? extends Object> refc = receiver.getClass(); // may get NPE
             MemberName method = resolveOrFail(refc, name, type, false);
             checkMethod(refc, method, false);
             MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClassOrNull());
             MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver);
             if (bmh == null)
-                throw newNoAccessException(method, lookupClass());
+                throw newNoAccessException(method, this);
             if (dmh.type().parameterCount() == 0)
                 return dmh;  // bound the trailing parameter; no varargs possible
             return fixVarargs(bmh, dmh);
@@ -772,9 +795,10 @@
          * the method's variable arity modifier bit ({@code 0x0080}) is set.
          * @param m the reflected method
          * @return a method handle which can invoke the reflected method
-         * @exception NoAccessException if access checking fails
+         * @throws IllegalAccessException if access checking fails
+         * @throws NullPointerException if the argument is null
          */
-        public MethodHandle unreflect(Method m) throws NoAccessException {
+        public MethodHandle unreflect(Method m) throws IllegalAccessException {
             MemberName method = new MemberName(m);
             assert(method.isMethod());
             if (!m.isAccessible())  checkMethod(method.getDeclaringClass(), method, method.isStatic());
@@ -799,9 +823,10 @@
          * @param m the reflected method
          * @param specialCaller the class nominally calling the method
          * @return a method handle which can invoke the reflected method
-         * @exception NoAccessException if access checking fails
+         * @throws IllegalAccessException if access checking fails
+         * @throws NullPointerException if any argument is null
          */
-        public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws NoAccessException {
+        public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
             checkSpecialCaller(specialCaller);
             MemberName method = new MemberName(m);
             assert(method.isMethod());
@@ -827,9 +852,10 @@
          * the constructor's variable arity modifier bit ({@code 0x0080}) is set.
          * @param c the reflected constructor
          * @return a method handle which can invoke the reflected constructor
-         * @exception NoAccessException if access checking fails
+         * @throws IllegalAccessException if access checking fails
+         * @throws NullPointerException if the argument is null
          */
-        public MethodHandle unreflectConstructor(Constructor c) throws NoAccessException {
+        public MethodHandle unreflectConstructor(Constructor c) throws IllegalAccessException {
             MemberName ctor = new MemberName(c);
             assert(ctor.isConstructor());
             if (!c.isAccessible())  checkAccess(c.getDeclaringClass(), ctor);
@@ -849,9 +875,10 @@
          * access checking is performed immediately on behalf of the lookup class.
          * @param f the reflected field
          * @return a method handle which can load values from the reflected field
-         * @exception NoAccessException if access checking fails
+         * @throws IllegalAccessException if access checking fails
+         * @throws NullPointerException if the argument is null
          */
-        public MethodHandle unreflectGetter(Field f) throws NoAccessException {
+        public MethodHandle unreflectGetter(Field f) throws IllegalAccessException {
             return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), false);
         }
 
@@ -866,40 +893,47 @@
          * access checking is performed immediately on behalf of the lookup class.
          * @param f the reflected field
          * @return a method handle which can store values into the reflected field
-         * @exception NoAccessException if access checking fails
+         * @throws IllegalAccessException if access checking fails
+         * @throws NullPointerException if the argument is null
          */
-        public MethodHandle unreflectSetter(Field f) throws NoAccessException {
+        public MethodHandle unreflectSetter(Field f) throws IllegalAccessException {
             return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), true);
         }
 
         /// Helper methods, all package-private.
 
-        MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) throws NoAccessException {
+        MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) throws NoSuchFieldException, IllegalAccessException {
             checkSymbolicClass(refc);  // do this before attempting to resolve
+            name.getClass(); type.getClass();  // NPE
             int mods = (isStatic ? Modifier.STATIC : 0);
-            return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull());
+            return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull(),
+                                            NoSuchFieldException.class);
         }
 
-        MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) throws NoAccessException {
+        MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) throws NoSuchMethodException, IllegalAccessException {
             checkSymbolicClass(refc);  // do this before attempting to resolve
+            name.getClass(); type.getClass();  // NPE
             int mods = (isStatic ? Modifier.STATIC : 0);
-            return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull());
+            return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull(),
+                                            NoSuchMethodException.class);
         }
 
         MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic,
-                                 boolean searchSupers, Class<?> specialCaller) throws NoAccessException {
+                                 boolean searchSupers, Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
             checkSymbolicClass(refc);  // do this before attempting to resolve
+            name.getClass(); type.getClass();  // NPE
             int mods = (isStatic ? Modifier.STATIC : 0);
-            return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller);
+            return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller,
+                                            NoSuchMethodException.class);
         }
 
-        void checkSymbolicClass(Class<?> refc) throws NoAccessException {
+        void checkSymbolicClass(Class<?> refc) throws IllegalAccessException {
             Class<?> caller = lookupClassOrNull();
             if (caller != null && !VerifyAccess.isClassAccessible(refc, caller))
-                throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), caller);
+                throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), this);
         }
 
-        void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) throws NoAccessException {
+        void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) throws IllegalAccessException {
             String message;
             if (m.isConstructor())
                 message = "expected a method, not a constructor";
@@ -909,10 +943,10 @@
                 message = wantStatic ? "expected a static method" : "expected a non-static method";
             else
                 { checkAccess(refc, m); return; }
-            throw newNoAccessException(message, m, lookupClass());
+            throw newNoAccessException(message, m, this);
         }
 
-        void checkAccess(Class<?> refc, MemberName m) throws NoAccessException {
+        void checkAccess(Class<?> refc, MemberName m) throws IllegalAccessException {
             int allowedModes = this.allowedModes;
             if (allowedModes == TRUSTED)  return;
             int mods = m.getModifiers();
@@ -927,22 +961,25 @@
                 && VerifyAccess.isSamePackage(m.getDeclaringClass(), lookupClass()))
                 // Protected members can also be checked as if they were package-private.
                 return;
-            throw newNoAccessException(accessFailedMessage(refc, m), m, lookupClass());
+            throw newNoAccessException(accessFailedMessage(refc, m), m, this);
         }
 
         String accessFailedMessage(Class<?> refc, MemberName m) {
             Class<?> defc = m.getDeclaringClass();
             int mods = m.getModifiers();
-            if (!VerifyAccess.isClassAccessible(defc, lookupClass()))
+            // check the class first:
+            boolean classOK = (Modifier.isPublic(defc.getModifiers()) &&
+                               (defc == refc ||
+                                Modifier.isPublic(refc.getModifiers())));
+            if (!classOK && (allowedModes & PACKAGE) != 0) {
+                classOK = (VerifyAccess.isClassAccessible(defc, lookupClass()) &&
+                           (defc == refc ||
+                            VerifyAccess.isClassAccessible(refc, lookupClass())));
+            }
+            if (!classOK)
                 return "class is not public";
-            if (refc != defc && !VerifyAccess.isClassAccessible(refc, lookupClass()))
-                return "symbolic reference "+refc.getName()+" is not public";
             if (Modifier.isPublic(mods))
                 return "access to public member failed";  // (how?)
-            else if (allowedModes == PUBLIC)
-                return "member is not public";
-            else if (allowedModes == 0)
-                return "attempted member access through a non-public class";
             if (Modifier.isPrivate(mods))
                 return "member is private";
             if (Modifier.isProtected(mods))
@@ -952,17 +989,17 @@
 
         private static final boolean ALLOW_NESTMATE_ACCESS = false;
 
-        void checkSpecialCaller(Class<?> specialCaller) throws NoAccessException {
+        void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
             if (allowedModes == TRUSTED)  return;
             if ((allowedModes & PRIVATE) == 0
                 || (specialCaller != lookupClass()
                     && !(ALLOW_NESTMATE_ACCESS &&
                          VerifyAccess.isSamePackageMember(specialCaller, lookupClass()))))
                 throw newNoAccessException("no private access for invokespecial",
-                                           new MemberName(specialCaller), lookupClass());
+                                           new MemberName(specialCaller), this);
         }
 
-        MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws NoAccessException {
+        MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws IllegalAccessException {
             // The accessing class only has the right to use a protected member
             // on itself or a subclass.  Enforce that restriction, from JVMS 5.4.4, etc.
             if (!method.isProtected() || method.isStatic()
@@ -974,7 +1011,7 @@
             else
                 return restrictReceiver(method, mh, lookupClass());
         }
-        MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws NoAccessException {
+        MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws IllegalAccessException {
             assert(!method.isStatic());
             Class<?> defc = method.getDeclaringClass();  // receiver type of mh is too wide
             if (defc.isInterface() || !defc.isAssignableFrom(caller)) {
@@ -988,18 +1025,18 @@
         }
 
         MethodHandle makeAccessor(Class<?> refc, String name, Class<?> type,
-                                  boolean isStatic, boolean isSetter) throws NoAccessException {
+                                  boolean isStatic, boolean isSetter) throws NoSuchFieldException, IllegalAccessException {
             MemberName field = resolveOrFail(refc, name, type, isStatic);
             if (isStatic != field.isStatic())
                 throw newNoAccessException(isStatic
                                            ? "expected a static field"
                                            : "expected a non-static field",
-                                           field, lookupClass());
+                                           field, this);
             return makeAccessor(refc, field, false, isSetter);
         }
 
         MethodHandle makeAccessor(Class<?> refc, MemberName field,
-                                  boolean trusted, boolean isSetter) throws NoAccessException {
+                                  boolean trusted, boolean isSetter) throws IllegalAccessException {
             assert(field.isField());
             if (trusted)
                 return MethodHandleImpl.accessField(IMPL_TOKEN, field, isSetter, lookupClassOrNull());
--- a/jdk/src/share/classes/java/dyn/NoAccessException.java	Fri Feb 11 01:26:28 2011 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package java.dyn;
-
-/**
- * Thrown to indicate that a caller has attempted to create a method handle
- * which accesses a field, method, or class to which the caller does not have access.
- * This unchecked exception is analogous to {@link IllegalAccessException},
- * which is a checked exception thrown when reflective invocation fails
- * because of an access check.  With method handles, this same access
- * checking is performed by the {@link MethodHandles.Lookup lookup object}
- * on behalf of the method handle creator,
- * at the time of creation.
- * @author John Rose, JSR 292 EG
- * @since 1.7
- */
-public class NoAccessException extends ReflectiveOperationException {
-    private static final long serialVersionUID = 292L;
-
-    /**
-     * Constructs a {@code NoAccessException} with no detail message.
-     */
-    public NoAccessException() {
-        super();
-    }
-
-    /**
-     * Constructs a {@code NoAccessException} with the specified
-     * detail message.
-     *
-     * @param s the detail message
-     */
-    public NoAccessException(String s) {
-        super(s);
-    }
-
-    /**
-     * Constructs a {@code NoAccessException} with the specified cause.
-     *
-     * @param cause the underlying cause of the exception
-     */
-    public NoAccessException(Throwable cause) {
-        super(cause);
-    }
-
-    /**
-     * Constructs a {@code NoAccessException} with the specified
-     * detail message and cause.
-     *
-     * @param s the detail message
-     * @param cause the underlying cause of the exception
-     */
-    public NoAccessException(String s, Throwable cause) {
-        super(s, cause);
-    }
-}
--- a/jdk/src/share/classes/sun/dyn/CallSiteImpl.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/CallSiteImpl.java	Fri Feb 11 01:26:32 2011 -0800
@@ -129,7 +129,7 @@
                 MethodType.methodType(void.class,
                                       String.class, MethodType.class,
                                       MemberName.class, int.class));
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             throw uncaughtException(ex);
         }
     }
--- a/jdk/src/share/classes/sun/dyn/FilterGeneric.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/FilterGeneric.java	Fri Feb 11 01:26:32 2011 -0800
@@ -187,7 +187,7 @@
             MethodHandle entryPoint = null;
             try {
                 entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
             }
             if (entryPoint == null)  continue;
             Constructor<? extends Adapter> ctor = null;
--- a/jdk/src/share/classes/sun/dyn/FilterOneArgument.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/FilterOneArgument.java	Fri Feb 11 01:26:32 2011 -0800
@@ -56,7 +56,7 @@
             INVOKE =
                 MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke",
                                                          MethodType.genericMethodType(1));
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             throw uncaughtException(ex);
         }
     }
--- a/jdk/src/share/classes/sun/dyn/FromGeneric.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/FromGeneric.java	Fri Feb 11 01:26:32 2011 -0800
@@ -204,7 +204,7 @@
             MethodHandle entryPoint = null;
             try {
                 entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
             }
             if (entryPoint == null)  continue;
             Constructor<? extends Adapter> ctor = null;
--- a/jdk/src/share/classes/sun/dyn/InvokeGeneric.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/InvokeGeneric.java	Fri Feb 11 01:26:32 2011 -0800
@@ -44,7 +44,7 @@
     /** Compute and cache information for this adapter, so that it can
      *  call out to targets of the erasure-family of the given erased type.
      */
-    private InvokeGeneric(MethodType erasedCallerType) throws NoAccessException {
+    private InvokeGeneric(MethodType erasedCallerType) throws ReflectiveOperationException {
         this.erasedCallerType = erasedCallerType;
         this.initialInvoker = makeInitialInvoker();
         assert initialInvoker.type().equals(erasedCallerType
@@ -64,14 +64,14 @@
             try {
                 InvokeGeneric gen = new InvokeGeneric(form.erasedType());
                 form.genericInvoker = genericInvoker = gen.initialInvoker;
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
                 throw new RuntimeException(ex);
             }
         }
         return genericInvoker;
     }
 
-    private MethodHandle makeInitialInvoker() throws NoAccessException {
+    private MethodHandle makeInitialInvoker() throws ReflectiveOperationException {
         // postDispatch = #(MH'; MT, MH; A...){MH'(MT, MH; A)}
         MethodHandle postDispatch = makePostDispatchInvoker();
         MethodHandle invoker;
@@ -95,7 +95,7 @@
         return MethodHandles.dropArguments(targetInvoker, 1, EXTRA_ARGS);
     }
 
-    private MethodHandle dispatcher(String dispatchName) throws NoAccessException {
+    private MethodHandle dispatcher(String dispatchName) throws ReflectiveOperationException {
         return lookup().bind(this, dispatchName,
                              MethodType.methodType(MethodHandle.class,
                                                    MethodType.class, MethodHandle.class));
--- a/jdk/src/share/classes/sun/dyn/Invokers.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/Invokers.java	Fri Feb 11 01:26:32 2011 -0800
@@ -69,7 +69,7 @@
         if (invoker != null)  return invoker;
         try {
             invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invokeExact", targetType);
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             throw new InternalError("JVM cannot find invoker for "+targetType);
         }
         assert(invokerType(targetType) == invoker.type());
@@ -128,7 +128,7 @@
                 THROW_UCS = MethodHandleImpl.IMPL_LOOKUP
                     .findStatic(CallSite.class, "uninitializedCallSite",
                                 MethodType.methodType(Empty.class));
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
                 throw new RuntimeException(ex);
             }
         }
--- a/jdk/src/share/classes/sun/dyn/MemberName.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/MemberName.java	Fri Feb 11 01:26:32 2011 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -451,8 +451,6 @@
             return type.toString();  // class java.lang.String
         // else it is a field, method, or constructor
         StringBuilder buf = new StringBuilder();
-        if (!isResolved())
-            buf.append("*.");
         if (getDeclaringClass() != null) {
             buf.append(getName(clazz));
             buf.append('.');
@@ -512,14 +510,24 @@
     public static RuntimeException newIllegalArgumentException(String message) {
         return new IllegalArgumentException(message);
     }
-    public static NoAccessException newNoAccessException(MemberName name, Class<?> lookupClass) {
-        return newNoAccessException("cannot access", name, lookupClass);
+    public static IllegalAccessException newNoAccessException(MemberName name, Object from) {
+        return newNoAccessException("cannot access", name, from);
+    }
+    public static IllegalAccessException newNoAccessException(String message,
+            MemberName name, Object from) {
+        message += ": " + name;
+        if (from != null)  message += ", from " + from;
+        return new IllegalAccessException(message);
     }
-    public static NoAccessException newNoAccessException(String message,
-            MemberName name, Class<?> lookupClass) {
-        message += ": " + name;
-        if (lookupClass != null)  message += ", from " + lookupClass.getName();
-        return new NoAccessException(message);
+    public static ReflectiveOperationException newNoAccessException(MemberName name) {
+        if (name.isResolved())
+            return new IllegalAccessException(name.toString());
+        else if (name.isConstructor())
+            return new NoSuchMethodException(name.toString());
+        else if (name.isMethod())
+            return new NoSuchMethodException(name.toString());
+        else
+            return new NoSuchFieldException(name.toString());
     }
     public static Error uncaughtException(Exception ex) {
         Error err = new InternalError("uncaught exception");
@@ -643,14 +651,20 @@
         /** Produce a resolved version of the given member.
          *  Super types are searched (for inherited members) if {@code searchSupers} is true.
          *  Access checking is performed on behalf of the given {@code lookupClass}.
-         *  If lookup fails or access is not permitted, a {@linkplain NoAccessException} is thrown.
+         *  If lookup fails or access is not permitted, a {@linkplain ReflectiveOperationException} is thrown.
          *  Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
          */
-        public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) throws NoAccessException {
+        public
+        <NoSuchMemberException extends ReflectiveOperationException>
+        MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass,
+                                 Class<NoSuchMemberException> nsmClass)
+                throws IllegalAccessException, NoSuchMemberException {
             MemberName result = resolveOrNull(m, searchSupers, lookupClass);
             if (result != null)
                 return result;
-            throw newNoAccessException(m, lookupClass);
+            ReflectiveOperationException ex = newNoAccessException(m);
+            if (ex instanceof IllegalAccessException)  throw (IllegalAccessException) ex;
+            throw nsmClass.cast(ex);
         }
         /** Return a list of all methods defined by the given class.
          *  Super types are searched (for inherited members) if {@code searchSupers} is true.
--- a/jdk/src/share/classes/sun/dyn/MethodHandleImpl.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/MethodHandleImpl.java	Fri Feb 11 01:26:32 2011 -0800
@@ -30,7 +30,6 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import sun.dyn.util.VerifyType;
-import java.dyn.NoAccessException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -169,11 +168,11 @@
      * @param doDispatch whether the method handle will test the receiver type
      * @param lookupClass access-check relative to this class
      * @return a direct handle to the matching method
-     * @throws NoAccessException if the given method cannot be accessed by the lookup class
+     * @throws IllegalAccessException if the given method cannot be accessed by the lookup class
      */
     public static
     MethodHandle findMethod(Access token, MemberName method,
-            boolean doDispatch, Class<?> lookupClass) throws NoAccessException {
+                            boolean doDispatch, Class<?> lookupClass) throws IllegalAccessException {
         Access.check(token);  // only trusted calls
         MethodType mtype = method.getMethodType();
         if (!method.isStatic()) {
@@ -307,7 +306,7 @@
                 MethodHandle invoke = null;
                 try {
                     invoke = lookup.findVirtual(AllocateObject.class, name, MethodType.genericMethodType(nargs));
-                } catch (NoAccessException ex) {
+                } catch (ReflectiveOperationException ex) {
                 }
                 if (invoke == null)  break;
                 invokes.add(invoke);
@@ -322,7 +321,7 @@
         static {
             try {
                 VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true));
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
                 throw uncaughtException(ex);
             }
         }
@@ -474,7 +473,7 @@
             MethodHandle mh;
             try {
                 mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
                 throw uncaughtException(ex);
             }
             if (evclass != vclass || (!isStatic && ecclass != cclass)) {
@@ -542,7 +541,7 @@
             MethodHandle mh;
             try {
                 mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type);
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
                 throw uncaughtException(ex);
             }
             if (caclass != null) {
@@ -1014,7 +1013,7 @@
                 MethodHandle invoke = null;
                 try {
                     invoke = lookup.findVirtual(GuardWithTest.class, name, MethodType.genericMethodType(nargs));
-                } catch (NoAccessException ex) {
+                } catch (ReflectiveOperationException ex) {
                 }
                 if (invoke == null)  break;
                 invokes.add(invoke);
@@ -1029,7 +1028,7 @@
         static {
             try {
                 VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithTest.class, "invoke_V", MethodType.genericMethodType(0, true));
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
                 throw uncaughtException(ex);
             }
         }
@@ -1150,7 +1149,7 @@
                 MethodHandle invoke = null;
                 try {
                     invoke = lookup.findVirtual(GuardWithCatch.class, name, MethodType.genericMethodType(nargs));
-                } catch (NoAccessException ex) {
+                } catch (ReflectiveOperationException ex) {
                 }
                 if (invoke == null)  break;
                 invokes.add(invoke);
@@ -1165,7 +1164,7 @@
         static {
             try {
                 VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true));
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
                 throw uncaughtException(ex);
             }
         }
@@ -1212,7 +1211,7 @@
             THROW_EXCEPTION
             = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
                     MethodType.methodType(Empty.class, Throwable.class));
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             throw new RuntimeException(ex);
         }
     }
--- a/jdk/src/share/classes/sun/dyn/MethodHandleNatives.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/MethodHandleNatives.java	Fri Feb 11 01:26:32 2011 -0800
@@ -350,7 +350,7 @@
             case REF_invokeInterface:   return lookup.findVirtual(      defc, name, (MethodType) type );
             }
             throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type);
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             Error err = new IncompatibleClassChangeError();
             err.initCause(ex);
             throw err;
--- a/jdk/src/share/classes/sun/dyn/SpreadGeneric.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/SpreadGeneric.java	Fri Feb 11 01:26:32 2011 -0800
@@ -167,7 +167,7 @@
             MethodHandle entryPoint = null;
             try {
                 entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
             }
             if (entryPoint == null)  continue;
             Constructor<? extends Adapter> ctor = null;
--- a/jdk/src/share/classes/sun/dyn/ToGeneric.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/ToGeneric.java	Fri Feb 11 01:26:32 2011 -0800
@@ -285,7 +285,7 @@
                 try {
                     entryPoint = MethodHandleImpl.IMPL_LOOKUP.
                                     findSpecial(acls, iname, entryPointType, acls);
-                } catch (NoAccessException ex) {
+                } catch (ReflectiveOperationException ex) {
                 }
                 if (entryPoint == null)  continue;
                 Constructor<? extends Adapter> ctor = null;
--- a/jdk/src/share/classes/sun/dyn/util/ValueConversions.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/util/ValueConversions.java	Fri Feb 11 01:26:32 2011 -0800
@@ -153,7 +153,7 @@
             try {
                 // actually, type is wrong; the Java method takes Object
                 mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
                 mh = null;
             }
         } else {
@@ -289,7 +289,7 @@
         if (exact) {
             try {
                 mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
                 mh = null;
             }
         } else {
@@ -408,7 +408,7 @@
         if (exact) {
             try {
                 mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
                 mh = null;
             }
         } else {
@@ -492,7 +492,7 @@
             case INT: case LONG: case FLOAT: case DOUBLE:
                 try {
                     mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
-                } catch (NoAccessException ex) {
+                } catch (ReflectiveOperationException ex) {
                     mh = null;
                 }
                 break;
@@ -654,7 +654,7 @@
             type = type.appendParameterTypes(wrap.primitiveType());
         try {
             mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type);
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             mh = null;
         }
         if (mh == null && wrap == Wrapper.VOID) {
@@ -723,7 +723,7 @@
             MethodHandle array = null;
             try {
                 array = lookup.findStatic(ValueConversions.class, name, type);
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
             }
             if (array == null)  break;
             arrays.add(array);
@@ -784,7 +784,7 @@
             MethodHandle array = null;
             try {
                 array = lookup.findStatic(ValueConversions.class, name, type);
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
             }
             if (array == null)  break;
             arrays.add(array);
--- a/jdk/src/share/classes/sun/dyn/util/VerifyAccess.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/util/VerifyAccess.java	Fri Feb 11 01:26:32 2011 -0800
@@ -25,7 +25,6 @@
 
 package sun.dyn.util;
 
-import java.dyn.NoAccessException;
 import java.lang.reflect.Modifier;
 import sun.dyn.MemberName;
 import sun.dyn.MethodHandleImpl;
@@ -139,6 +138,8 @@
      * <li>C is public.
      * <li>C and D are members of the same runtime package.
      * </ul>
+     * @param refc the symbolic reference class to which access is being checked (C)
+     * @param lookupClass the class performing the lookup (D)
      */
     public static boolean isClassAccessible(Class<?> refc, Class<?> lookupClass) {
         int mods = refc.getModifiers();
--- a/jdk/test/java/dyn/InvokeGenericTest.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/test/java/dyn/InvokeGenericTest.java	Fri Feb 11 01:26:32 2011 -0800
@@ -338,7 +338,7 @@
                 = LOOKUP.findStatic(LOOKUP.lookupClass(),
                                     "collector",
                                     methodType(Object.class, Object[].class));
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             throw new RuntimeException(ex);
         }
     }
--- a/jdk/test/java/dyn/JavaDocExamplesTest.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/test/java/dyn/JavaDocExamplesTest.java	Fri Feb 11 01:26:32 2011 -0800
@@ -74,7 +74,7 @@
 // static final private MethodHandle HASHCODE_1 = LOOKUP.findVirtual(Object.class,
 //     "hashCode", methodType(int.class));
 
-// form required if NoAccessException is intercepted:
+// form required if ReflectiveOperationException is intercepted:
 static final private MethodHandle CONCAT_2, HASHCODE_2;
 static {
   try {
@@ -82,7 +82,7 @@
       "concat", methodType(String.class, String.class));
     HASHCODE_2 = LOOKUP.findVirtual(Object.class,
       "hashCode", methodType(int.class));
-   } catch (NoAccessException ex) {
+   } catch (ReflectiveOperationException ex) {
      throw new RuntimeException(ex);
    }
 }
--- a/jdk/test/java/dyn/MethodHandlesTest.java	Fri Feb 11 01:26:28 2011 -0800
+++ b/jdk/test/java/dyn/MethodHandlesTest.java	Fri Feb 11 01:26:32 2011 -0800
@@ -496,8 +496,12 @@
         try {
             if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
             target = lookup.in(defc).findStatic(defc, name, type);
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             noAccess = ex;
+            if (name.contains("bogus"))
+                assertTrue(noAccess instanceof NoSuchMethodException);
+            else
+                assertTrue(noAccess instanceof IllegalAccessException);
         }
         if (verbosity >= 3)
             System.out.println("findStatic "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
@@ -566,8 +570,12 @@
         try {
             if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
             target = lookup.in(defc).findVirtual(defc, methodName, type);
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             noAccess = ex;
+            if (name.contains("bogus"))
+                assertTrue(noAccess instanceof NoSuchMethodException);
+            else
+                assertTrue(noAccess instanceof IllegalAccessException);
         }
         if (verbosity >= 3)
             System.out.println("findVirtual "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
@@ -596,11 +604,12 @@
         testFindSpecial(SubExample.class, Example.class, void.class, "v0");
         testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0");
         // Do some negative testing:
+        testFindSpecial(false, EXAMPLE, SubExample.class, Example.class, void.class, "bogus");
+        testFindSpecial(false, PRIVATE, SubExample.class, Example.class, void.class, "bogus");
         for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) {
             testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0");
             testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", int.class);
             testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0");
-            testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "bogus");
         }
     }
 
@@ -621,8 +630,12 @@
             if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
             if (verbosity >= 5)  System.out.println("  lookup => "+lookup.in(specialCaller));
             target = lookup.in(specialCaller).findSpecial(defc, name, type, specialCaller);
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             noAccess = ex;
+            if (name.contains("bogus"))
+                assertTrue(noAccess instanceof NoSuchMethodException);
+            else
+                assertTrue(noAccess instanceof IllegalAccessException);
         }
         if (verbosity >= 3)
             System.out.println("findSpecial from "+specialCaller.getName()+" to "+defc.getName()+"."+name+"/"+type+" => "+target
@@ -677,8 +690,12 @@
         try {
             if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
             target = lookup.in(defc).bind(receiver, methodName, type);
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             noAccess = ex;
+            if (name.contains("bogus"))
+                assertTrue(noAccess instanceof NoSuchMethodException);
+            else
+                assertTrue(noAccess instanceof IllegalAccessException);
         }
         if (verbosity >= 3)
             System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target
@@ -736,14 +753,9 @@
                                    Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable {
         countTest(positive);
         MethodType type = MethodType.methodType(ret, params);
-        Method rmethod = null;
+        Method rmethod = defc.getDeclaredMethod(name, params);
         MethodHandle target = null;
         Exception noAccess = null;
-        try {
-            rmethod = defc.getDeclaredMethod(name, params);
-        } catch (NoSuchMethodException ex) {
-            throw new NoAccessException(ex);
-        }
         boolean isStatic = (rcvc == null);
         boolean isSpecial = (specialCaller != null);
         try {
@@ -752,8 +764,12 @@
                 target = lookup.in(specialCaller).unreflectSpecial(rmethod, specialCaller);
             else
                 target = lookup.in(defc).unreflect(rmethod);
-        } catch (NoAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
             noAccess = ex;
+            if (name.contains("bogus"))
+                assertTrue(noAccess instanceof NoSuchMethodException);
+            else
+                assertTrue(noAccess instanceof IllegalAccessException);
         }
         if (verbosity >= 3)
             System.out.println("unreflect"+(isSpecial?"Special":"")+" "+defc.getName()+"."+name+"/"+type
@@ -862,25 +878,28 @@
                     if (type == float.class) {
                         float v = 'F';
                         if (isStatic)  v++;
-                        assert(value.equals(v));
+                        assertTrue(value.equals(v));
                     }
-                    assert(name.equals(field.getName()));
-                    assert(type.equals(field.getType()));
-                    assert(isStatic == (Modifier.isStatic(field.getModifiers())));
+                    assertTrue(name.equals(field.getName()));
+                    assertTrue(type.equals(field.getType()));
+                    assertTrue(isStatic == (Modifier.isStatic(field.getModifiers())));
                     cases.add(new Object[]{ field, value });
                 }
             }
+            cases.add(new Object[]{ new Object[]{ false, HasFields.class, "bogus_fD", double.class }, Error.class });
+            cases.add(new Object[]{ new Object[]{ true,  HasFields.class, "bogus_sL", Object.class }, Error.class });
             CASES = cases.toArray(new Object[0][]);
         }
     }
 
-    static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC_FIELD = 3;
+    static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC = 3, TEST_SETTER = 0x10;
     static boolean testModeMatches(int testMode, boolean isStatic) {
         switch (testMode) {
-        case TEST_FIND_STATIC_FIELD:    return isStatic;
+        case TEST_FIND_STATIC:          return isStatic;
         case TEST_FIND_FIELD:           return !isStatic;
-        default:                        return true;  // unreflect matches both
+        case TEST_UNREFLECT:            return true;  // unreflect matches both
         }
+        throw new InternalError("testMode="+testMode);
     }
 
     @Test
@@ -896,54 +915,161 @@
     @Test
     public void testFindStaticGetter() throws Throwable {
         startTest("findStaticGetter");
-        testGetter(TEST_FIND_STATIC_FIELD);
+        testGetter(TEST_FIND_STATIC);
     }
     public void testGetter(int testMode) throws Throwable {
         Lookup lookup = PRIVATE;  // FIXME: test more lookups than this one
         for (Object[] c : HasFields.CASES) {
-            Field f = (Field)c[0];
-            Object value = c[1];
-            Class<?> type = f.getType();
-            testGetter(lookup, f, type, value, testMode);
+            boolean positive = (c[1] != Error.class);
+            testGetter(positive, lookup, c[0], c[1], testMode);
+        }
+        testGetter(true, lookup,
+                   new Object[]{ true,  System.class, "out", java.io.PrintStream.class },
+                   System.out, testMode);
+        for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
+            testGetter(false, lookup,
+                       new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
+                       null, testMode);
         }
     }
-    public void testGetter(MethodHandles.Lookup lookup,
-            Field f, Class<?> type, Object value, int testMode) throws Throwable {
-        boolean isStatic = Modifier.isStatic(f.getModifiers());
-        Class<?> fclass = f.getDeclaringClass();
-        String   fname  = f.getName();
-        Class<?> ftype  = f.getType();
+    public void testGetter(boolean positive, MethodHandles.Lookup lookup,
+                           Object fieldRef, Object value, int testMode) throws Throwable {
+        testAccessor(positive, lookup, fieldRef, value, testMode);
+    }
+
+    public void testAccessor(boolean positive, MethodHandles.Lookup lookup,
+                             Object fieldRef, Object value, int testMode0) throws Throwable {
+        boolean isGetter = ((testMode0 & TEST_SETTER) == 0);
+        int testMode = testMode0 & ~TEST_SETTER;
+        boolean isStatic;
+        Class<?> fclass;
+        String   fname;
+        Class<?> ftype;
+        Field f = (fieldRef instanceof Field ? (Field)fieldRef : null);
+        if (f != null) {
+            isStatic = Modifier.isStatic(f.getModifiers());
+            fclass   = f.getDeclaringClass();
+            fname    = f.getName();
+            ftype    = f.getType();
+        } else {
+            Object[] scnt = (Object[]) fieldRef;
+            isStatic = (Boolean)  scnt[0];
+            fclass   = (Class<?>) scnt[1];
+            fname    = (String)   scnt[2];
+            ftype    = (Class<?>) scnt[3];
+            try {
+                f = fclass.getDeclaredField(fname);
+            } catch (ReflectiveOperationException ex) {
+                f = null;
+            }
+        }
         if (!testModeMatches(testMode, isStatic))  return;
-        countTest(true);
-        MethodType expType = MethodType.methodType(type, HasFields.class);
+        if (f == null && testMode == TEST_UNREFLECT)  return;
+        countTest(positive);
+        MethodType expType;
+        if (isGetter)
+            expType = MethodType.methodType(ftype, HasFields.class);
+        else
+            expType = MethodType.methodType(void.class, HasFields.class, ftype);
         if (isStatic)  expType = expType.dropParameterTypes(0, 1);
-        MethodHandle mh = lookup.unreflectGetter(f);
+        Exception noAccess = null;
+        MethodHandle mh;
+        try {
+            switch (testMode0) {
+            case TEST_UNREFLECT:   mh = lookup.unreflectGetter(f);                      break;
+            case TEST_FIND_FIELD:  mh = lookup.findGetter(fclass, fname, ftype);        break;
+            case TEST_FIND_STATIC: mh = lookup.findStaticGetter(fclass, fname, ftype);  break;
+            case TEST_SETTER|
+                 TEST_UNREFLECT:   mh = lookup.unreflectSetter(f);                      break;
+            case TEST_SETTER|
+                 TEST_FIND_FIELD:  mh = lookup.findSetter(fclass, fname, ftype);        break;
+            case TEST_SETTER|
+                 TEST_FIND_STATIC: mh = lookup.findStaticSetter(fclass, fname, ftype);  break;
+            default:
+                throw new InternalError("testMode="+testMode);
+            }
+        } catch (ReflectiveOperationException ex) {
+            mh = null;
+            noAccess = ex;
+            if (fname.contains("bogus"))
+                assertTrue(noAccess instanceof NoSuchFieldException);
+            else
+                assertTrue(noAccess instanceof IllegalAccessException);
+        }
+        if (verbosity >= 3)
+            System.out.println("find"+(isStatic?"Static":"")+(isGetter?"Getter":"Setter")+" "+fclass.getName()+"."+fname+"/"+ftype
+                               +" => "+mh
+                               +(noAccess == null ? "" : " !! "+noAccess));
+        if (positive && noAccess != null)  throw new RuntimeException(noAccess);
+        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, mh != null);
+        if (!positive)  return; // negative test failed as expected
+        assertEquals((isStatic ? 0 : 1)+(isGetter ? 0 : 1), mh.type().parameterCount());
+
+
         assertSame(mh.type(), expType);
         assertNameStringContains(mh, fname);
         HasFields fields = new HasFields();
         Object sawValue;
-        Class<?> rtype = type;
-        if (type != int.class)  rtype = Object.class;
-        mh = MethodHandles.convertArguments(mh, mh.type().generic().changeReturnType(rtype));
-        Object expValue = value;
-        for (int i = 0; i <= 1; i++) {
-            if (isStatic) {
-                if (type == int.class)
-                    sawValue = (int) mh.invokeExact();  // do these exactly
-                else
-                    sawValue = mh.invokeExact();
-            } else {
-                if (type == int.class)
-                    sawValue = (int) mh.invokeExact((Object) fields);
-                else
-                    sawValue = mh.invokeExact((Object) fields);
+        Class<?> vtype = ftype;
+        if (ftype != int.class)  vtype = Object.class;
+        if (isGetter) {
+            mh = MethodHandles.convertArguments(mh, mh.type().generic()
+                                                .changeReturnType(vtype));
+        } else {
+            int last = mh.type().parameterCount() - 1;
+            mh = MethodHandles.convertArguments(mh, mh.type().generic()
+                                                .changeReturnType(void.class)
+                                                .changeParameterType(last, vtype));
+        }
+        if (f != null && f.getDeclaringClass() == HasFields.class) {
+            assertEquals(f.get(fields), value);  // clean to start with
+        }
+        if (isGetter) {
+            Object expValue = value;
+            for (int i = 0; i <= 1; i++) {
+                if (isStatic) {
+                    if (ftype == int.class)
+                        sawValue = (int) mh.invokeExact();  // do these exactly
+                    else
+                        sawValue = mh.invokeExact();
+                } else {
+                    if (ftype == int.class)
+                        sawValue = (int) mh.invokeExact((Object) fields);
+                    else
+                        sawValue = mh.invokeExact((Object) fields);
+                }
+                assertEquals(sawValue, expValue);
+                if (f != null && f.getDeclaringClass() == HasFields.class
+                    && !Modifier.isFinal(f.getModifiers())) {
+                    Object random = randomArg(ftype);
+                    f.set(fields, random);
+                    expValue = random;
+                } else {
+                    break;
+                }
             }
-            assertEquals(sawValue, expValue);
-            Object random = randomArg(type);
-            f.set(fields, random);
-            expValue = random;
+        } else {
+            for (int i = 0; i <= 1; i++) {
+                Object putValue = randomArg(ftype);
+                if (isStatic) {
+                    if (ftype == int.class)
+                        mh.invokeExact((int)putValue);  // do these exactly
+                    else
+                        mh.invokeExact(putValue);
+                } else {
+                    if (ftype == int.class)
+                        mh.invokeExact((Object) fields, (int)putValue);
+                    else
+                        mh.invokeExact((Object) fields, putValue);
+                }
+                if (f != null && f.getDeclaringClass() == HasFields.class) {
+                    assertEquals(f.get(fields), putValue);
+                }
+            }
         }
-        f.set(fields, value);  // put it back
+        if (f != null && f.getDeclaringClass() == HasFields.class) {
+            f.set(fields, value);  // put it back
+        }
     }
 
 
@@ -960,61 +1086,24 @@
     @Test
     public void testFindStaticSetter() throws Throwable {
         startTest("findStaticSetter");
-        testSetter(TEST_FIND_STATIC_FIELD);
+        testSetter(TEST_FIND_STATIC);
     }
     public void testSetter(int testMode) throws Throwable {
         Lookup lookup = PRIVATE;  // FIXME: test more lookups than this one
         startTest("unreflectSetter");
         for (Object[] c : HasFields.CASES) {
-            Field f = (Field)c[0];
-            Object value = c[1];
-            Class<?> type = f.getType();
-            testSetter(lookup, f, type, value, testMode);
+            boolean positive = (c[1] != Error.class);
+            testSetter(positive, lookup, c[0], c[1], testMode);
+        }
+        for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
+            testSetter(false, lookup,
+                       new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
+                       null, testMode);
         }
     }
-    public void testSetter(MethodHandles.Lookup lookup,
-            Field f, Class<?> type, Object value, int testMode) throws Throwable {
-        boolean isStatic = Modifier.isStatic(f.getModifiers());
-        Class<?> fclass = f.getDeclaringClass();
-        String   fname  = f.getName();
-        Class<?> ftype  = f.getType();
-        if (!testModeMatches(testMode, isStatic))  return;
-        countTest(true);
-        MethodType expType = MethodType.methodType(void.class, HasFields.class, type);
-        if (isStatic)  expType = expType.dropParameterTypes(0, 1);
-        MethodHandle mh;
-        if (testMode == TEST_UNREFLECT)
-            mh = lookup.unreflectSetter(f);
-        else if (testMode == TEST_FIND_FIELD)
-            mh = lookup.findSetter(fclass, fname, ftype);
-        else if (testMode == TEST_FIND_STATIC_FIELD)
-            mh = lookup.findStaticSetter(fclass, fname, ftype);
-        else  throw new InternalError();
-        assertSame(mh.type(), expType);
-        assertNameStringContains(mh, fname);
-        HasFields fields = new HasFields();
-        Object sawValue;
-        Class<?> vtype = type;
-        if (type != int.class)  vtype = Object.class;
-        int last = mh.type().parameterCount() - 1;
-        mh = MethodHandles.convertArguments(mh, mh.type().generic().changeReturnType(void.class).changeParameterType(last, vtype));
-        assertEquals(f.get(fields), value);  // clean to start with
-        for (int i = 0; i <= 1; i++) {
-            Object putValue = randomArg(type);
-            if (isStatic) {
-                if (type == int.class)
-                    mh.invokeExact((int)putValue);  // do these exactly
-                else
-                    mh.invokeExact(putValue);
-            } else {
-                if (type == int.class)
-                    mh.invokeExact((Object) fields, (int)putValue);
-                else
-                    mh.invokeExact((Object) fields, putValue);
-            }
-            assertEquals(f.get(fields), putValue);
-        }
-        f.set(fields, value);  // put it back
+    public void testSetter(boolean positive, MethodHandles.Lookup lookup,
+                           Object fieldRef, Object value, int testMode) throws Throwable {
+        testAccessor(positive, lookup, fieldRef, value, testMode | TEST_SETTER);
     }
 
     @Test
@@ -1323,7 +1412,7 @@
                 types[i] = args[i].getClass();
         }
         int inargs = args.length, outargs = reorder.length;
-        assert(inargs == types.length);
+        assertTrue(inargs == types.length);
         if (verbosity >= 3)
             System.out.println("permuteArguments "+Arrays.toString(reorder));
         Object[] permArgs = new Object[outargs];
@@ -2219,12 +2308,13 @@
             MethodHandle array = null;
             try {
                 array = lookup.findStatic(ValueConversions.class, name, type);
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
+                // break from loop!
             }
             if (array == null)  break;
             arrays.add(array);
         }
-        assert(arrays.size() == 11);  // current number of methods
+        assertTrue(arrays.size() == 11);  // current number of methods
         return arrays.toArray(new MethodHandle[0]);
     }
     static final MethodHandle[] ARRAYS = makeArrays();
@@ -2280,12 +2370,13 @@
             MethodHandle list = null;
             try {
                 list = lookup.findStatic(ValueConversions.class, name, type);
-            } catch (NoAccessException ex) {
+            } catch (ReflectiveOperationException ex) {
+                // break from loop!
             }
             if (list == null)  break;
             lists.add(list);
         }
-        assert(lists.size() == 11);  // current number of methods
+        assertTrue(lists.size() == 11);  // current number of methods
         return lists.toArray(new MethodHandle[0]);
     }
     static final MethodHandle[] LISTS = makeLists();