changeset 444:469cff19f299

meth-info-8008688.patch: tweak names & exceptions
author jrose
date Wed, 15 May 2013 23:31:58 -0700
parents eabaf978d9ee
children 08add1bcd8eb
files meth-info-8008688.patch
diffstat 1 files changed, 241 insertions(+), 121 deletions(-) [+]
line wrap: on
line diff
--- a/meth-info-8008688.patch	Tue May 14 22:58:15 2013 -0700
+++ b/meth-info-8008688.patch	Wed May 15 23:31:58 2013 -0700
@@ -24,7 +24,7 @@
 new file mode 100644
 --- /dev/null
 +++ b/src/share/classes/java/lang/invoke/InfoFromMemberName.java
-@@ -0,0 +1,111 @@
+@@ -0,0 +1,135 @@
 +/*
 + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -62,11 +62,13 @@
 + * Auxiliary to MethodHandleInfo, wants to nest in MethodHandleInfo but must be non-public.
 + */
 +/*non-public*/
++final
 +class InfoFromMemberName implements MethodHandleInfo {
 +    private final MemberName member;
 +    private final int referenceKind;
 +
 +    InfoFromMemberName(Lookup lookup, MemberName member, byte referenceKind) {
++        assert(member.isResolved());
 +        assert(member.referenceKindIsConsistentWith(referenceKind));
 +        this.member = member;
 +        this.referenceKind = referenceKind;
@@ -89,8 +91,6 @@
 +
 +    @Override
 +    public int getModifiers() {
-+        if (member == null || !member.isResolved())
-+            return -1;  // API contract
 +        return member.getModifiers();
 +    }
 +
@@ -99,42 +99,66 @@
 +        return referenceKind;
 +    }
 +
-+    @Override public String toString() { return MethodHandleInfo.toString(this); }
-+    //@Override public boolean equals(Object x) { return MethodHandleInfo.equals(this, x); }
-+    //@Override public int hashCode() { return MethodHandleInfo.hashCode(this); }
-+
-+    /*package*/
-+    static NoSuchMethodError notDirectMethodHandle() {
-+        return new NoSuchMethodError("not a direct method handle");
++    @Override
++    public String toString() {
++        return MethodHandleInfo.toString(getReferenceKind(), getDeclaringClass(), getName(), getMethodType());
 +    }
 +
-+    // Helper for default MethodHandleInfo.asMember.
-+    static Member reflectMember(MethodHandleInfo self) throws ReflectiveOperationException {
-+        int mods = self.getModifiers();
-+        if (mods == -1)  throw notDirectMethodHandle();
-+        byte refKind = (byte) self.getReferenceKind();
-+        Class<?> defc = self.getDeclaringClass();
-+        boolean isPublic = Modifier.isPublic(mods);
++    @Override
++    public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup) {
++        Member mem = AccessController.doPrivileged(new PrivilegedAction<Member>() {
++                public Member run() {
++                    try {
++                        return reflectUnchecked();
++                    } catch (ReflectiveOperationException ex) {
++                        throw new IllegalArgumentException(ex);
++                    }
++                }
++            });
++        try {
++            checkAccess(mem, lookup);
++        } catch (IllegalAccessException ex) {
++            throw new IllegalArgumentException(ex);
++        }
++        return expected.cast(mem);
++    }
++
++    private Member reflectUnchecked() throws ReflectiveOperationException {
++        byte refKind = (byte) getReferenceKind();
++        Class<?> defc = getDeclaringClass();
++        boolean isPublic = Modifier.isPublic(getModifiers());
 +        if (MethodHandleNatives.refKindIsMethod(refKind)) {
 +            if (isPublic)
-+                return defc.getMethod(self.getName(), self.getMethodType().parameterArray());
++                return defc.getMethod(getName(), getMethodType().parameterArray());
 +            else
-+                return defc.getDeclaredMethod(self.getName(), self.getMethodType().parameterArray());
++                return defc.getDeclaredMethod(getName(), getMethodType().parameterArray());
 +        } else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
 +            if (isPublic)
-+                return defc.getConstructor(self.getMethodType().parameterArray());
++                return defc.getConstructor(getMethodType().parameterArray());
 +            else
-+                return defc.getDeclaredConstructor(self.getMethodType().parameterArray());
++                return defc.getDeclaredConstructor(getMethodType().parameterArray());
 +        } else if (MethodHandleNatives.refKindIsField(refKind)) {
 +            if (isPublic)
-+                return defc.getField(self.getName());
++                return defc.getField(getName());
 +            else
-+                return defc.getDeclaredField(self.getName());
++                return defc.getDeclaredField(getName());
 +        } else {
 +            throw new IllegalArgumentException("referenceKind="+refKind);
 +        }
 +    }
 +
++    private void checkAccess(Member mem, Lookup lookup) throws IllegalAccessException {
++        byte refKind = (byte) getReferenceKind();
++        if (mem instanceof Method) {
++            boolean wantSpecial = (refKind == REF_invokeSpecial);
++            lookup.checkAccess(refKind, getDeclaringClass(), new MemberName((Method) mem, wantSpecial));
++        } else if (mem instanceof Constructor) {
++            lookup.checkAccess(refKind, getDeclaringClass(), new MemberName((Constructor) mem));
++        } else if (mem instanceof Field) {
++            boolean isSetter = (refKind == REF_putField || refKind == REF_putStatic);
++            lookup.checkAccess(refKind, getDeclaringClass(), new MemberName((Field) mem, isSetter));
++        }
++    }
 +}
 diff --git a/src/share/classes/java/lang/invoke/MemberName.java b/src/share/classes/java/lang/invoke/MemberName.java
 --- a/src/share/classes/java/lang/invoke/MemberName.java
@@ -189,7 +213,7 @@
 diff --git a/src/share/classes/java/lang/invoke/MethodHandleInfo.java b/src/share/classes/java/lang/invoke/MethodHandleInfo.java
 --- a/src/share/classes/java/lang/invoke/MethodHandleInfo.java
 +++ b/src/share/classes/java/lang/invoke/MethodHandleInfo.java
-@@ -24,80 +24,210 @@
+@@ -24,80 +24,211 @@
   */
  
  package java.lang.invoke;
@@ -275,6 +299,7 @@
 + *     <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
 + * </tr>
 + * </table>
++ * @since 1.8
   */
 -final class MethodHandleInfo {
 -   public static final int
@@ -376,11 +401,34 @@
 -   public int getModifiers() {
 -       return -1; //TODO
 -   }
--
++    /**
++     * Reflects the underlying member as a method, constructor, or field object.
++     * If the underlying member is public, it is reflected as if by
++     * {@code getMethod}, {@code getConstructor}, or {@code getField}.
++     * Otherwise, it is reflected as if by
++     * {@code getDeclaredMethod}, {@code getDeclaredConstructor}, or {@code getDeclaredField}.
++     * The underlying member must be accessible to the given lookup object.
++     * @param <T> the desired type of the result, either {@link Member} or a subtype
++     * @param expected a class object representing the desired result type {@code T}
++     * @param lookup the lookup object that created this MethodHandleInfo, or one with equivalent access privileges
++     * @return a reference to the method, constructor, or field object
++     * @exception ClassCastException if the member is not of the expected type
++     * @exception IllegalArgumentException if the underlying member is not accessible to the given lookup object
++     */
++    public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup);
+ 
 -   public int getReferenceKind() {
 -       return referenceKind;
 -   }
--
++    /**
++     * Returns the access modifiers of the underlying member.
++     * @return the Java language modifiers for underlying member,
++     *         or -1 if the member cannot be accessed
++     * @see Modifier
++     * @see reflectAs
++     */
++    public int getModifiers();
+ 
 -   static String getReferenceKindString(int referenceKind) {
 -        switch (referenceKind) {
 -            case REF_getField: return "getfield";
@@ -395,27 +443,17 @@
 -            default: return "UNKNOWN_REFENCE_KIND" + "[" + referenceKind + "]";
 -        }
 +    /**
-+     * Reflects the underlying member as a method, constructor, or field object.
-+     * If the underlying member is public, it is reflected as if by
-+     * {@code getMethod}, {@code getConstructor}, or {@code getField}.
-+     * Otherwise, it is reflected as if by
-+     * {@code getDeclaredMethod}, {@code getDeclaredConstructor}, or {@code getDeclaredField}.
-+     * @param <T> the desired type of the result, either {@link Member} or a subtype
-+     * @param expected a class object representing the desired result type {@code T}
-+     * @return a reference to the method, constructor, or field object
-+     * @exception ClassCastException if the member is not of the expected type
++     * Returns the descriptive name of the given reference kind,
++     * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
++     * The conventional prefix "REF_" is omitted.
++     * @param referenceKind an integer code for a kind of reference used to access a class member
++     * @return a mixed-case string such as {@code "getField"}
++     * @exception IllegalArgumentException if the given number is not a reference kind
 +     */
-+    public default <T extends Member> T asMember(Class<T> expected) {
-+        Member mem = AccessController.doPrivileged(new PrivilegedAction<Member>() {
-+                public Member run() {
-+                    try {
-+                        return InfoFromMemberName.reflectMember(MethodHandleInfo.this);
-+                    } catch (ReflectiveOperationException ex) {
-+                        throw newInternalError(ex);
-+                    }
-+                }
-+            });
-+        return expected.cast(mem);
++    public static String referenceKindToString(int referenceKind) {
++        if (!MethodHandleNatives.refKindIsValid(referenceKind))
++            throw newIllegalArgumentException("invalid reference kind", referenceKind);
++        return MethodHandleNatives.refKindName((byte)referenceKind);
      }
  
 -    @Override
@@ -423,43 +461,28 @@
 -        return String.format("%s %s.%s:%s", getReferenceKindString(referenceKind),
 -                             declaringClass.getName(), name, methodType);
 +    /**
-+     * Returns the access modifiers of the underlying member.
-+     * @return the Java language modifiers for underlying member,
-+     *         or -1 if the member cannot be accessed
-+     * @see Modifier
-+     */
-+    public default int getModifiers() {
-+        return asMember(Member.class).getModifiers();
-+    }
-+
-+    /**
-+     * Returns the descriptive name of the given reference kind,
-+     * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
-+     * The conventional prefix "REF_" is omitted.
-+     * @param referenceKind an integer code for a kind of reference used to access a class member
-+     * @return a mixed-case string such as {@code "getField"}
-+     * @exception IllegalArgumentException if the give number is not a reference kind
-+     */
-+    public static String getReferenceKindString(int referenceKind) {
-+        if (!MethodHandleNatives.refKindIsValid(referenceKind))
-+            throw newIllegalArgumentException("invalid reference kind", referenceKind);
-+        return MethodHandleNatives.refKindName((byte)referenceKind);
-+    }
-+
-+    /**
-+     * Returns a string representation for the symbolic reference to this method.
++     * Returns a string representation for a {@code MethodHandleInfo},
++     * a symbolic reference to a method, constructor, or field.
 +     * This is defined to be of the form {@code "RK C.N:MT"}, where {@code RK} is the
-+     * {@linkplain #getReferenceKindString reference kind string},
++     * {@linkplain #referenceKindToString reference kind string},
 +     * {@code C} is the {@linkplain java.lang.Class#getName name} of the
 +     * {@linkplain #getDeclaringClass declaring class},
 +     * {@code N} is the {@linkplain #getName name}, and
 +     * {@code MT} is the {@linkplain #getMethodType method type} of this reference.
-+     * @param self this symbolic reference
++     * @implSpec
++     * This produces a result equivalent to:
++     * <pre>{@code
++     *     String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type)
++     * }</pre>
++     *
++     * @param kind the reference kind of this symbolic reference
++     * @param defc the declared class of this symbolic reference
++     * @param name the reference kind of this symbolic reference
++     * @param type the reference kind of this symbolic reference
 +     * @return a string of the form {@code "RK C.N:MT"}
 +     */
-+    public static String toString(MethodHandleInfo self) {
-+        return String.format("%s %s.%s:%s", getReferenceKindString(self.getReferenceKind()),
-+                             self.getDeclaringClass().getName(), self.getName(), self.getMethodType());
++    public static String toString(int kind, Class<?> defc, String name, MethodType type) {
++        return String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type);
      }
  }
 diff --git a/src/share/classes/java/lang/invoke/MethodHandleNatives.java b/src/share/classes/java/lang/invoke/MethodHandleNatives.java
@@ -488,7 +511,15 @@
  import java.util.List;
  import java.util.ArrayList;
  import java.util.Arrays;
-@@ -92,6 +91,44 @@
+@@ -51,6 +50,7 @@
+  * </ul>
+  * <p>
+  * @author John Rose, JSR 292 EG
++ * @since 1.7
+  */
+ public class MethodHandles {
+ 
+@@ -94,6 +94,38 @@
      }
  
      /**
@@ -497,7 +528,7 @@
 +     * to crack the target method handle, called
 +     * {@link java.lang.invoke.MethodHandles.Lookup#revealDirect Lookup.revealDirect}
 +     * on the target to obtain its symbolic reference, and then called
-+     * {@link java.lang.invoke.MethodHandleInfo#asMember MethodHandleInfo.asMember}
++     * {@link java.lang.invoke.MethodHandleInfo#reflectAs MethodHandleInfo.reflectAs}
 +     * to resolve the symbolic reference to a member.
 +     * <p>
 +     * If there is a security manager, its {@code checkPermission} method
@@ -507,23 +538,17 @@
 +     * @param expected a class object representing the desired result type {@code T}
 +     * @return the method, constructor, or field
 +     * @exception SecurityException if the caller is not privileged to call {@code setAccessible}
-+     * @throws NoSuchMethodException if the target is not a direct method handle
++     * @exception NullPointerException if either argument is {@code null}
++     * @exception IllegalArgumentException if the target is not a direct method handle
 +     * @exception ClassCastException if the member is not of the expected type
++     * @since 1.8
 +     */
 +    public static <T extends Member> T
-+    revealDirectAsAccessibleObject(MethodHandle target, Class<T> expected)
-+        throws NoSuchMethodException
-+    {
++    reflectAs(Class<T> expected, MethodHandle target) {
 +        SecurityManager smgr = System.getSecurityManager();
 +        if (smgr != null)  smgr.checkPermission(ACCESS_PERMISSION);
-+        try {
-+            return Lookup.IMPL_LOOKUP.revealDirect(target).asMember(expected);
-+        } catch (NoSuchMethodException ex) {
-+            throw ex;
-+        } catch (ReflectiveOperationException ex) {
-+            // Privileged lookup object should not fail this way.
-+            throw newInternalError(ex);
-+        }
++        Lookup lookup = Lookup.IMPL_LOOKUP;  // use maximally privileged lookup
++        return lookup.revealDirect(target).reflectAs(expected, lookup);
 +    }
 +    // Copied from AccessibleObject, as used by Method.setAccessible, etc.:
 +    static final private java.security.Permission ACCESS_PERMISSION =
@@ -533,7 +558,7 @@
       * A <em>lookup object</em> is a factory for creating method handles,
       * when the creation requires access checking.
       * Method handles do not perform
-@@ -1022,6 +1059,42 @@
+@@ -1027,6 +1059,46 @@
              return unreflectField(f, true);
          }
  
@@ -547,17 +572,17 @@
 +         * @return a symbolic reference which can be used to reconstruct this method handle from this lookup object
 +         * @exception SecurityException if a security manager is present and it
 +         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
-+         * @throws NoSuchMethodException if the target is not a direct method handle
-+         * @throws IllegalAccessException if access checking fails
++         * @throws IllegalArgumentException if the target is not a direct method handle or if access checking fails
++         * @exception NullPointerException if the target is {@code null}
++         * @since 1.8
 +         */
-+        public MethodHandleInfo revealDirect(MethodHandle target)
-+            throws IllegalAccessException, NoSuchMethodException
-+        {
++        public MethodHandleInfo revealDirect(MethodHandle target) {
 +            MemberName member = target.internalMemberName();
 +            if (member == null || !member.isResolved())
-+                throw InfoFromMemberName.notDirectMethodHandle();
++                throw newIllegalArgumentException("not a direct method handle");
 +            Class<?> defc = member.getDeclaringClass();
 +            byte refKind = member.getReferenceKind();
++            assert(MethodHandleNatives.refKindIsValid(refKind));
 +            if (refKind == REF_invokeSpecial && !target.isInvokeSpecial())
 +                // Devirtualized method invocation is usually formally virtual.
 +                // To avoid creating extra MemberName objects for this common case,
@@ -567,8 +592,12 @@
 +                // Symbolic reference is through interface but resolves to Object method (toString, etc.)
 +                refKind = REF_invokeInterface;
 +            // Check SM permissions and member access before cracking.
-+            checkSecurityManager(defc, member, lookupClass());
-+            checkAccess(member.getReferenceKind(), defc, member);
++            try {
++                checkSecurityManager(defc, member, lookupClass());
++                checkAccess(member.getReferenceKind(), defc, member);
++            } catch (IllegalAccessException ex) {
++                throw new IllegalArgumentException(ex);
++            }
 +            // Produce the handle to the results.
 +            return new InfoFromMemberName(this, member, refKind);
 +        }
@@ -576,7 +605,7 @@
          /// Helper methods, all package-private.
  
          MemberName resolveOrFail(byte refKind, Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
-@@ -1264,6 +1337,7 @@
+@@ -1269,6 +1341,7 @@
              Class<?> refcAsSuper;
              if (refKind == REF_invokeSpecial &&
                  refc != lookupClass() &&
@@ -584,7 +613,7 @@
                  refc != (refcAsSuper = lookupClass().getSuperclass()) &&
                  refc.isAssignableFrom(lookupClass())) {
                  assert(!method.getName().equals("<init>"));  // not this code path
-@@ -1292,6 +1366,7 @@
+@@ -1297,6 +1370,7 @@
              return mh;
          }
          private MethodHandle fakeMethodHandleInvoke(MemberName method) {
@@ -592,7 +621,7 @@
              return throwException(method.getReturnType(), UnsupportedOperationException.class);
          }
          private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
-@@ -1304,6 +1379,7 @@
+@@ -1309,6 +1383,7 @@
                  hostClass = callerClass;  // callerClass came from a security manager style stack walk
              MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass);
              // Note: caller will apply varargs after this step happens.
@@ -600,7 +629,7 @@
              return cbmh;
          }
          private MethodHandle getDirectField(byte refKind, Class<?> refc, MemberName field) throws IllegalAccessException {
-@@ -1468,6 +1544,7 @@
+@@ -1474,6 +1549,7 @@
       */
      static public
      MethodHandle exactInvoker(MethodType type) {
@@ -608,7 +637,7 @@
          return type.invokers().exactInvoker();
      }
  
-@@ -1501,6 +1578,7 @@
+@@ -1507,6 +1583,7 @@
       */
      static public
      MethodHandle invoker(MethodType type) {
@@ -619,7 +648,7 @@
 diff --git a/src/share/classes/java/lang/reflect/Modifier.java b/src/share/classes/java/lang/reflect/Modifier.java
 --- a/src/share/classes/java/lang/reflect/Modifier.java
 +++ b/src/share/classes/java/lang/reflect/Modifier.java
-@@ -147,12 +147,33 @@
+@@ -147,12 +147,34 @@
       * @param   mod a set of modifiers
       * @return {@code true} if {@code mod} includes the
       * {@code transient} modifier; {@code false} otherwise.
@@ -644,6 +673,7 @@
 +     * @return {@code true} if {@code mod} specifies
 +     * a variable number of arguments; {@code false} otherwise.
 +     * @see #isTransient
++     * @since 1.8
 +     */
 +    public static boolean isVarArgs(int mod) {
 +        return (mod & VARARGS) != 0;
@@ -653,7 +683,7 @@
       * Return {@code true} if the integer argument includes the
       * {@code native} modifier, {@code false} otherwise.
       *
-@@ -332,12 +353,22 @@
+@@ -332,12 +354,23 @@
       */
      public static final int STRICT           = 0x00000800;
  
@@ -662,6 +692,7 @@
 +     * takes a variable number of arguments.
 +     * <p>
 +     * Note: This value is also used to declare {@code transient} fields.
++     * @since 1.8
 +     */
 +    public static final int VARARGS          = 0x00000080;
 +    static {
@@ -681,7 +712,7 @@
 new file mode 100644
 --- /dev/null
 +++ b/test/java/lang/invoke/RevealDirectTest.java
-@@ -0,0 +1,342 @@
+@@ -0,0 +1,431 @@
 +/*
 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -768,10 +799,24 @@
 +    static boolean VERBOSE = false;
 +
 +    @Test public void testSimple() throws Throwable {
-+        testOnClass(Simple.localLookup(), Simple.class, Simple.MEMBERS);
++        if (VERBOSE)  System.out.println("@Test testSimple");
++        testOnClass(Simple.localLookup(), null,
++                    Simple.class, Simple.MEMBERS);
++    }
++    @Test public void testSimpleNegative() throws Throwable {
++        if (VERBOSE)  System.out.println("@Test testSimpleNegative");
++        testOnClass(Simple.localLookup(), publicLookup(),
++                    Simple.class, nonPublicOnly(Simple.MEMBERS));
 +    }
 +    @Test public void testPublicLookup() throws Throwable {
-+        testOnClass(publicLookup(), Simple.class, publicOnly(Simple.MEMBERS));
++        if (VERBOSE)  System.out.println("@Test testPublicLookup");
++        testOnClass(publicLookup(), null,
++                    Simple.class, publicOnly(Simple.MEMBERS));
++    }
++    @Test public void testPublicLookupNegative() throws Throwable {
++        if (VERBOSE)  System.out.println("@Test testPublicLookupNegative");
++        testOnClass(publicLookup(), publicLookup(),
++                    Simple.class, nonPublicOnly(Simple.MEMBERS));
 +    }
 +
 +    static List<Member> getMembers(Class<?> cls) {
@@ -814,38 +859,83 @@
 +    }
 +
 +    static List<Member> publicOnly(List<Member> members) {
++        return removeMods(members, Modifier.PUBLIC, 0);
++    }
++    static List<Member> nonPublicOnly(List<Member> members) {
++        return removeMods(members, Modifier.PUBLIC, -1);
++    }
++    static List<Member> removeMods(List<Member> members, int mask, int bits) {
++        int publicMods = (mask & Modifier.PUBLIC);
 +        members = new ArrayList<>(members);
 +        for (Iterator<Member> i = members.iterator(); i.hasNext(); ) {
 +            Member mem = i.next();
 +            int mods = mem.getModifiers();
-+            mods &= mem.getDeclaringClass().getModifiers();
-+            if (!Modifier.isPublic(mods))
++            if ((publicMods & mods) != 0 &&
++                (publicMods & mem.getDeclaringClass().getModifiers()) == 0)
++                mods -= publicMods;
++            if ((mods & mask) == (bits & mask))
 +                i.remove();
 +        }
 +        return members;
 +    }
 +
-+    void testOnClass(Lookup lookup, Class<?> cls) throws Throwable {
-+        testOnClass(lookup, cls, getMembers(cls));
-+    }
-+    void testOnClass(Lookup lookup, Class<?> cls, List<Member> mems) throws Throwable {
++    void testOnClass(Lookup lookup, Lookup negLookup,
++                     Class<?> cls, List<Member> mems) throws Throwable {
 +        for (Member mem : mems) {
-+            testWithMember(lookup, mem);
++            testWithMember(lookup, negLookup, mem);
 +        }
 +    }
 +    static class UnreflectResult {  // a tuple
 +        final MethodHandle mh;
++        final ReflectiveOperationException ex;
 +        final byte kind;
 +        final Member mem;
 +        final int var;
 +        UnreflectResult(MethodHandle mh, byte kind, Member mem, int var) {
 +            this.mh = mh;
++            this.ex = null;
 +            this.kind = kind;
 +            this.mem = mem;
 +            this.var = var;
 +        }
++        UnreflectResult(ReflectiveOperationException ex, byte kind, Member mem, int var) {
++            this.mh = null;
++            this.ex = ex;
++            this.kind = kind;
++            this.mem = mem;
++            this.var = var;
++        }
++        public String toString() {
++            return MethodHandleInfo.toString(kind, mem.getDeclaringClass(), name(mem), type(mem, kind));
++        }
++        static String name(Member mem) {
++            if (mem instanceof Constructor)  return "<init>";
++            return mem.getName();
++        }
++        static MethodType type(Member mem, byte kind) {
++            if (mem instanceof Field) {
++                Class<?> type = ((Field)mem).getType();
++                if (kind == REF_putStatic || kind == REF_putField)
++                    return methodType(void.class, type);
++                return methodType(type);
++            }
++            Class<?>[] params = ((Executable)mem).getParameterTypes();
++            if (mem instanceof Constructor)
++                return methodType(void.class, params);
++            Class<?> type = ((Method)mem).getReturnType();
++            return methodType(type, params);
++        }
 +    }
-+    static UnreflectResult unreflectMember(Lookup lookup, Member mem, int variation) throws ReflectiveOperationException {
++    static UnreflectResult unreflectMember(Lookup lookup, Member mem, int variation) {
++        byte[] refKind = {0};
++        try {
++            return unreflectMemberOrThrow(lookup, mem, variation, refKind);
++        } catch (ReflectiveOperationException ex) {
++            return new UnreflectResult(ex, refKind[0], mem, variation);
++        }
++    }
++    static UnreflectResult unreflectMemberOrThrow(Lookup lookup, Member mem, int variation,
++                                                  byte[] refKind) throws ReflectiveOperationException {
 +        Class<?> cls = lookup.lookupClass();
 +        Class<?> defc = mem.getDeclaringClass();
 +        String   name = mem.getName();
@@ -868,6 +958,7 @@
 +                kind = REF_invokeInterface;
 +            else
 +                kind = REF_invokeVirtual;
++            refKind[0] = kind;
 +            switch (variation) {
 +            case 0:
 +                if (isStatic)
@@ -894,6 +985,7 @@
 +            Constructor<?> m = (Constructor<?>) mem;
 +            MethodType type = methodType(void.class, m.getParameterTypes());
 +            kind = REF_newInvokeSpecial;
++            refKind[0] = kind;
 +            switch (variation) {
 +            case 0:
 +                mh = lookup.findConstructor(defc, type);
@@ -910,6 +1002,7 @@
 +                kind = (byte)(isStatic ? REF_putStatic : REF_putField);
 +            else
 +                kind = (byte)(isStatic ? REF_getStatic : REF_getField);
++            refKind[0] = kind;
 +            switch (variation) {
 +            case 0:
 +                if (isStatic)
@@ -964,6 +1057,7 @@
 +        return false;
 +    }
 +    static boolean consistent(UnreflectResult res, MethodHandleInfo info) {
++        assert(res.mh != null);
 +        assert(res.kind == info.getReferenceKind());
 +        assert(res.mem.getModifiers() == info.getModifiers());
 +        assert(res.mem.getDeclaringClass() == info.getDeclaringClass());
@@ -978,6 +1072,7 @@
 +            expectType = expectType.changeReturnType(void.class);
 +        assert(expectType.equals(info.getMethodType()));
 +        assert(res.mh.isVarargsCollector() == isVarArgs(info));
++        assert(res.toString().equals(info.toString()));
 +        return true;
 +    }
 +    static boolean isVarArgs(MethodHandleInfo info) {
@@ -1001,17 +1096,38 @@
 +        assert(mh.isVarargsCollector() == mh2.isVarargsCollector());
 +        return true;
 +    }
-+    void testWithMember(Lookup lookup, Member mem) throws Throwable {
++    void testWithMember(Lookup lookup, Lookup negLookup, Member mem) throws Throwable {
++        boolean hasSM = (System.getSecurityManager() != null);
++        boolean expectEx = (negLookup == lookup);
++        Lookup refLookup = (negLookup != null ? negLookup : lookup);
++        boolean expectEx2 = (refLookup == negLookup);
 +        for (int variation = 0; ; variation++) {
 +            UnreflectResult res = unreflectMember(lookup, mem, variation);
 +            if (res == null)  break;
 +            if (VERBOSE && variation == 0)
 +                System.out.println("from "+mem.getDeclaringClass().getSimpleName());
 +            MethodHandle mh = res.mh;
++            Throwable ex = res.ex;
++            if (VERBOSE)  System.out.println("  "+variation+": "+res+"  << "+(mh != null ? mh : ex));
++            if (expectEx && ex != null)
++                continue;  // this is OK; we expected that lookup to fail
++            if (expectEx)
++                throw new AssertionError("lookup succeeded unexpectedly for negative test");
++            if (ex != null && !expectEx) {
++                if (negLookup == null)  throw ex;
++                throw new AssertionError("lookup failed unexpectedly for negative test", ex);
++            }
 +            MethodHandleInfo info = lookup.revealDirect(mh);
-+            if (VERBOSE)  System.out.println("  "+variation+": "+info+"  << "+mh);
 +            assert(consistent(res, info));
-+            Member mem2 = info.asMember(Member.class);
++            Member mem2;
++            try {
++                mem2 = info.reflectAs(Member.class, refLookup);
++                if (expectEx2)  throw new AssertionError("reflection succeeded unexpectedly for negative test");
++            } catch (IllegalArgumentException ex2) {
++                if (VERBOSE)  System.out.println("  "+variation+": "+info+"  (REFLECT)<< "+ex2);
++                if (expectEx2)  continue;  // this is OK; we expected the reflect to fail
++                throw ex2;
++            }
 +            assert(consistent(mem, mem2));
 +            UnreflectResult res2 = unreflectMember(lookup, mem2, variation);
 +            MethodHandle mh2 = res2.mh;
@@ -1019,7 +1135,11 @@
 +            MethodHandleInfo info2 = lookup.revealDirect(mh2);
 +            assert(consistent(info, info2));
 +            assert(consistent(res, info2));
-+            Member mem3 = info2.asMember(Member.class);
++            Member mem3;
++            if (hasSM)
++                mem3 = info2.reflectAs(Member.class, lookup);
++            else
++                mem3 = MethodHandles.reflectAs(Member.class, mh2);
 +            assert(consistent(mem2, mem3));
 +        }
 +    }