changeset 9781:4bdbd1fabea4

8008688: Make MethodHandleInfo public Summary: A major overhaul to MethodHandleInfo and method handles in general. Reviewed-by: vlivanov, twisti Contributed-by: john.r.rose@oracle.com
author rfield
date Tue, 03 Sep 2013 21:42:56 -0700
parents 2cdd1078f45b
children 462c5589bc1a
files src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java src/share/classes/java/lang/invoke/InfoFromMemberName.java src/share/classes/java/lang/invoke/Invokers.java src/share/classes/java/lang/invoke/MemberName.java src/share/classes/java/lang/invoke/MethodHandle.java src/share/classes/java/lang/invoke/MethodHandleImpl.java src/share/classes/java/lang/invoke/MethodHandleInfo.java src/share/classes/java/lang/invoke/MethodHandleNatives.java src/share/classes/java/lang/invoke/MethodHandles.java src/share/classes/java/lang/invoke/SerializedLambda.java test/java/lang/invoke/7087570/Test7087570.java test/java/lang/invoke/RevealDirectTest.java test/java/lang/invoke/jtreg.security.policy
diffstat 13 files changed, 1437 insertions(+), 204 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java	Tue Sep 03 23:47:27 2013 -0400
+++ b/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java	Tue Sep 03 21:42:56 2013 -0700
@@ -124,7 +124,7 @@
         this.samMethodType  = samMethodType;
 
         this.implMethod = implMethod;
-        this.implInfo = new MethodHandleInfo(implMethod);
+        this.implInfo = caller.revealDirect(implMethod);
         // @@@ Temporary work-around pending resolution of 8005119
         this.implKind = (implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial)
                         ? MethodHandleInfo.REF_invokeVirtual
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/lang/invoke/InfoFromMemberName.java	Tue Sep 03 21:42:56 2013 -0700
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2012, 2013, 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.lang.invoke;
+
+import java.security.*;
+import java.lang.reflect.*;
+import java.lang.invoke.MethodHandleNatives.Constants;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandleStatics.*;
+
+/*
+ * 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() || member.isMethodHandleInvoke());
+        assert(member.referenceKindIsConsistentWith(referenceKind));
+        this.member = member;
+        this.referenceKind = referenceKind;
+    }
+
+    @Override
+    public Class<?> getDeclaringClass() {
+        return member.getDeclaringClass();
+    }
+
+    @Override
+    public String getName() {
+        return member.getName();
+    }
+
+    @Override
+    public MethodType getMethodType() {
+        return member.getMethodOrFieldType();
+    }
+
+    @Override
+    public int getModifiers() {
+        return member.getModifiers();
+    }
+
+    @Override
+    public int getReferenceKind() {
+        return referenceKind;
+    }
+
+    @Override
+    public String toString() {
+        return MethodHandleInfo.toString(getReferenceKind(), getDeclaringClass(), getName(), getMethodType());
+    }
+
+    @Override
+    public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup) {
+        if (member.isMethodHandleInvoke() && !member.isVarargs()) {
+            // This member is an instance of a signature-polymorphic method, which cannot be reflected
+            // A method handle invoker can come in either of two forms:
+            // A generic placeholder (present in the source code, and varargs)
+            // and a signature-polymorphic instance (synthetic and not varargs).
+            // For more information see comments on {@link MethodHandleNatives#linkMethod}.
+            throw new IllegalArgumentException("cannot reflect signature polymorphic method");
+        }
+        Member mem = AccessController.doPrivileged(new PrivilegedAction<Member>() {
+                public Member run() {
+                    try {
+                        return reflectUnchecked();
+                    } catch (ReflectiveOperationException ex) {
+                        throw new IllegalArgumentException(ex);
+                    }
+                }
+            });
+        try {
+            Class<?> defc = getDeclaringClass();
+            byte refKind = (byte) getReferenceKind();
+            lookup.checkAccess(refKind, defc, convertToMemberName(refKind, mem));
+        } 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(getName(), getMethodType().parameterArray());
+            else
+                return defc.getDeclaredMethod(getName(), getMethodType().parameterArray());
+        } else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
+            if (isPublic)
+                return defc.getConstructor(getMethodType().parameterArray());
+            else
+                return defc.getDeclaredConstructor(getMethodType().parameterArray());
+        } else if (MethodHandleNatives.refKindIsField(refKind)) {
+            if (isPublic)
+                return defc.getField(getName());
+            else
+                return defc.getDeclaredField(getName());
+        } else {
+            throw new IllegalArgumentException("referenceKind="+refKind);
+        }
+    }
+
+    private static MemberName convertToMemberName(byte refKind, Member mem) throws IllegalAccessException {
+        if (mem instanceof Method) {
+            boolean wantSpecial = (refKind == REF_invokeSpecial);
+            return new MemberName((Method) mem, wantSpecial);
+        } else if (mem instanceof Constructor) {
+            return new MemberName((Constructor) mem);
+        } else if (mem instanceof Field) {
+            boolean isSetter = (refKind == REF_putField || refKind == REF_putStatic);
+            return new MemberName((Field) mem, isSetter);
+        }
+        throw new InternalError(mem.getClass().getName());
+    }
+}
--- a/src/share/classes/java/lang/invoke/Invokers.java	Tue Sep 03 23:47:27 2013 -0400
+++ b/src/share/classes/java/lang/invoke/Invokers.java	Tue Sep 03 21:42:56 2013 -0700
@@ -87,6 +87,7 @@
             lform = invokeForm(mtype, true, MethodTypeForm.LF_EX_INVOKER);
             invoker = SimpleMethodHandle.make(invokerType, lform);
         }
+        invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invokeExact", mtype));
         assert(checkInvoker(invoker));
         exactInvoker = invoker;
         return invoker;
@@ -110,6 +111,7 @@
             lform = invokeForm(mtype, true, MethodTypeForm.LF_GEN_INVOKER);
             invoker = SimpleMethodHandle.make(invokerType, lform);
         }
+        invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invoke", mtype));
         assert(checkInvoker(invoker));
         generalInvoker = invoker;
         return invoker;
--- a/src/share/classes/java/lang/invoke/MemberName.java	Tue Sep 03 23:47:27 2013 -0400
+++ b/src/share/classes/java/lang/invoke/MemberName.java	Tue Sep 03 21:42:56 2013 -0700
@@ -320,14 +320,18 @@
 
     /** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */
     public boolean isMethodHandleInvoke() {
-        final int bits = Modifier.NATIVE | Modifier.FINAL;
+        final int bits = MH_INVOKE_MODS;
         final int negs = Modifier.STATIC;
         if (testFlags(bits | negs, bits) &&
             clazz == MethodHandle.class) {
-            return name.equals("invoke") || name.equals("invokeExact");
+            return isMethodHandleInvokeName(name);
         }
         return false;
     }
+    public static boolean isMethodHandleInvokeName(String name) {
+        return name.equals("invoke") || name.equals("invokeExact");
+    }
+    private static final int MH_INVOKE_MODS = Modifier.NATIVE | Modifier.FINAL | Modifier.PUBLIC;
 
     /** Utility method to query the modifier flags of this member. */
     public boolean isStatic() {
@@ -482,12 +486,27 @@
         m.getClass();  // NPE check
         // fill in vmtarget, vmindex while we have m in hand:
         MethodHandleNatives.init(this, m);
+        if (clazz == null) {  // MHN.init failed
+            if (m.getDeclaringClass() == MethodHandle.class &&
+                isMethodHandleInvokeName(m.getName())) {
+                // The JVM did not reify this signature-polymorphic instance.
+                // Need a special case here.
+                // See comments on MethodHandleNatives.linkMethod.
+                MethodType type = MethodType.methodType(m.getReturnType(), m.getParameterTypes());
+                int flags = flagsMods(IS_METHOD, m.getModifiers(), REF_invokeVirtual);
+                init(MethodHandle.class, m.getName(), type, flags);
+                if (isMethodHandleInvoke())
+                    return;
+            }
+            throw new LinkageError(m.toString());
+        }
         assert(isResolved() && this.clazz != null);
         this.name = m.getName();
         if (this.type == null)
             this.type = new Object[] { m.getReturnType(), m.getParameterTypes() };
         if (wantSpecial) {
-            assert(!isAbstract()) : this;
+            if (isAbstract())
+                throw new AbstractMethodError(this.toString());
             if (getReferenceKind() == REF_invokeVirtual)
                 changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
             else if (getReferenceKind() == REF_invokeInterface)
@@ -562,6 +581,22 @@
         initResolved(true);
     }
 
+    /**
+     * Create a name for a signature-polymorphic invoker.
+     * This is a placeholder for a signature-polymorphic instance
+     * (of MH.invokeExact, etc.) that the JVM does not reify.
+     * See comments on {@link MethodHandleNatives#linkMethod}.
+     */
+    static MemberName makeMethodHandleInvoke(String name, MethodType type) {
+        return makeMethodHandleInvoke(name, type, MH_INVOKE_MODS | SYNTHETIC);
+    }
+    static MemberName makeMethodHandleInvoke(String name, MethodType type, int mods) {
+        MemberName mem = new MemberName(MethodHandle.class, name, type, REF_invokeVirtual);
+        mem.flags |= mods;  // it's not resolved, but add these modifiers anyway
+        assert(mem.isMethodHandleInvoke()) : mem;
+        return mem;
+    }
+
     // bare-bones constructor; the JVM will fill it in
     MemberName() { }
 
--- a/src/share/classes/java/lang/invoke/MethodHandle.java	Tue Sep 03 23:47:27 2013 -0400
+++ b/src/share/classes/java/lang/invoke/MethodHandle.java	Tue Sep 03 21:42:56 2013 -0700
@@ -1285,6 +1285,11 @@
     }
 
     /*non-public*/
+    MethodHandle withInternalMemberName(MemberName member) {
+        return MethodHandleImpl.makeWrappedMember(this, member);
+    }
+
+    /*non-public*/
     boolean isInvokeSpecial() {
         return false;  // DMH.Special returns true
     }
@@ -1356,7 +1361,7 @@
     MethodHandle rebind() {
         // Bind 'this' into a new invoker, of the known class BMH.
         MethodType type2 = type();
-        LambdaForm form2 = reinvokerForm(type2.basicType());
+        LambdaForm form2 = reinvokerForm(this);
         // form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }
         return BoundMethodHandle.bindSingle(type2, form2, this);
     }
@@ -1369,23 +1374,38 @@
     /** Create a LF which simply reinvokes a target of the given basic type.
      *  The target MH must override {@link #reinvokerTarget} to provide the target.
      */
-    static LambdaForm reinvokerForm(MethodType mtype) {
-        mtype = mtype.basicType();
+    static LambdaForm reinvokerForm(MethodHandle target) {
+        MethodType mtype = target.type().basicType();
         LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
         if (reinvoker != null)  return reinvoker;
-        MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
+        if (mtype.parameterSlotCount() >= MethodType.MAX_MH_ARITY)
+            return makeReinvokerForm(target.type(), target);  // cannot cache this
+        reinvoker = makeReinvokerForm(mtype, null);
+        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, reinvoker);
+    }
+    private static LambdaForm makeReinvokerForm(MethodType mtype, MethodHandle customTargetOrNull) {
+        boolean customized = (customTargetOrNull != null);
+        MethodHandle MH_invokeBasic = customized ? null : MethodHandles.basicInvoker(mtype);
         final int THIS_BMH    = 0;
         final int ARG_BASE    = 1;
         final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
         int nameCursor = ARG_LIMIT;
-        final int NEXT_MH     = nameCursor++;
+        final int NEXT_MH     = customized ? -1 : nameCursor++;
         final int REINVOKE    = nameCursor++;
         LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
-        names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
-        Object[] targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
-        targetArgs[0] = names[NEXT_MH];  // overwrite this MH with next MH
-        names[REINVOKE] = new LambdaForm.Name(MH_invokeBasic, targetArgs);
-        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, new LambdaForm("BMH.reinvoke", ARG_LIMIT, names));
+        Object[] targetArgs;
+        MethodHandle targetMH;
+        if (customized) {
+            targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class);
+            targetMH = customTargetOrNull;
+        } else {
+            names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
+            targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
+            targetArgs[0] = names[NEXT_MH];  // overwrite this MH with next MH
+            targetMH = MethodHandles.basicInvoker(mtype);
+        }
+        names[REINVOKE] = new LambdaForm.Name(targetMH, targetArgs);
+        return new LambdaForm("BMH.reinvoke", ARG_LIMIT, names);
     }
 
     private static final LambdaForm.NamedFunction NF_reinvokerTarget;
--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Tue Sep 03 23:47:27 2013 -0400
+++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Tue Sep 03 21:42:56 2013 -0700
@@ -317,7 +317,7 @@
         private MethodHandle cache;
 
         AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) {
-            super(type, reinvokerForm(type));
+            super(type, reinvokerForm(target));
             this.target = target;
             this.arrayType = arrayType;
             this.cache = target.asCollector(arrayType, 0);
@@ -778,16 +778,27 @@
     }
     static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
 
-    static MethodHandle FAKE_METHOD_HANDLE_INVOKE;
-    static
-    MethodHandle fakeMethodHandleInvoke(MemberName method) {
-        MethodType type = method.getInvocationType();
-        assert(type.equals(MethodType.methodType(Object.class, Object[].class)));
-        MethodHandle mh = FAKE_METHOD_HANDLE_INVOKE;
+    static MethodHandle[] FAKE_METHOD_HANDLE_INVOKE = new MethodHandle[2];
+    static MethodHandle fakeMethodHandleInvoke(MemberName method) {
+        int idx;
+        assert(method.isMethodHandleInvoke());
+        switch (method.getName()) {
+        case "invoke":       idx = 0; break;
+        case "invokeExact":  idx = 1; break;
+        default:             throw new InternalError(method.getName());
+        }
+        MethodHandle mh = FAKE_METHOD_HANDLE_INVOKE[idx];
         if (mh != null)  return mh;
-        mh = throwException(type.insertParameterTypes(0, UnsupportedOperationException.class));
+        MethodType type = MethodType.methodType(Object.class, UnsupportedOperationException.class,
+                                                MethodHandle.class, Object[].class);
+        mh = throwException(type);
         mh = mh.bindTo(new UnsupportedOperationException("cannot reflectively invoke MethodHandle"));
-        FAKE_METHOD_HANDLE_INVOKE = mh;
+        if (!method.getInvocationType().equals(mh.type()))
+            throw new InternalError(method.toString());
+        mh = mh.withInternalMemberName(method);
+        mh = mh.asVarargsCollector(Object[].class);
+        assert(method.isVarargs());
+        FAKE_METHOD_HANDLE_INVOKE[idx] = mh;
         return mh;
     }
 
@@ -821,7 +832,7 @@
             MethodHandle vamh = prepareForInvoker(mh);
             // Cache the result of makeInjectedInvoker once per argument class.
             MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass);
-            return restoreToType(bccInvoker.bindTo(vamh), mh.type());
+            return restoreToType(bccInvoker.bindTo(vamh), mh.type(), mh.internalMemberName());
         }
 
         private static MethodHandle makeInjectedInvoker(Class<?> hostClass) {
@@ -876,8 +887,11 @@
         }
 
         // Undo the adapter effect of prepareForInvoker:
-        private static MethodHandle restoreToType(MethodHandle vamh, MethodType type) {
-            return vamh.asCollector(Object[].class, type.parameterCount()).asType(type);
+        private static MethodHandle restoreToType(MethodHandle vamh, MethodType type, MemberName member) {
+            MethodHandle mh = vamh.asCollector(Object[].class, type.parameterCount());
+            mh = mh.asType(type);
+            mh = mh.withInternalMemberName(member);
+            return mh;
         }
 
         private static final MethodHandle MH_checkCallerClass;
@@ -939,4 +953,41 @@
             }
         }
     }
+
+
+    /** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */
+    static class WrappedMember extends MethodHandle {
+        private final MethodHandle target;
+        private final MemberName member;
+
+        private WrappedMember(MethodHandle target, MethodType type, MemberName member) {
+            super(type, reinvokerForm(target));
+            this.target = target;
+            this.member = member;
+        }
+
+        @Override
+        MethodHandle reinvokerTarget() {
+            return target;
+        }
+        @Override
+        MemberName internalMemberName() {
+            return member;
+        }
+        @Override
+        boolean isInvokeSpecial() {
+            return target.isInvokeSpecial();
+        }
+        @Override
+        MethodHandle viewAsType(MethodType newType) {
+            return new WrappedMember(target, newType, member);
+        }
+    }
+
+    static MethodHandle makeWrappedMember(MethodHandle target, MemberName member) {
+        if (member.equals(target.internalMemberName()))
+            return target;
+        return new WrappedMember(target, target.type(), member);
+    }
+
 }
--- a/src/share/classes/java/lang/invoke/MethodHandleInfo.java	Tue Sep 03 23:47:27 2013 -0400
+++ b/src/share/classes/java/lang/invoke/MethodHandleInfo.java	Tue Sep 03 21:42:56 2013 -0700
@@ -24,80 +24,246 @@
  */
 
 package java.lang.invoke;
+
+import java.lang.reflect.*;
+import java.util.*;
 import java.lang.invoke.MethodHandleNatives.Constants;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandleStatics.*;
 
 /**
- * Cracking (reflecting) method handles back into their constituent symbolic parts.
+ * A symbolic reference obtained by cracking a method handle into its consitutent symbolic parts.
+ * To crack a direct method handle, call {@link Lookup#revealDirect Lookup.revealDirect}.
+ * <p>
+ * A <em>direct method handle</em> represents a method, constructor, or field without
+ * any intervening argument bindings or other transformations.
+ * The method, constructor, or field referred to by a direct method handle is called
+ * its <em>underlying member</em>.
+ * Direct method handles may be obtained in any of these ways:
+ * <ul>
+ * <li>By executing an {@code ldc} instruction on a {@code CONSTANT_MethodHandle} constant.
+ *     (See the Java Virtual Machine Specification, sections 4.4.8 and 5.4.3.)
+ * <li>By calling one of the <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>,
+ *     such as {@link Lookup#findVirtual Lookup.findVirtual},
+ *     to resolve a symbolic reference into a method handle.
+ *     A symbolic reference consists of a class, name string, and type.
+ * <li>By calling the factory method {@link Lookup#unreflect Lookup.unreflect}
+ *     or {@link Lookup#unreflectSpecial Lookup.unreflectSpecial}
+ *     to convert a {@link Method} into a method handle.
+ * <li>By calling the factory method {@link Lookup#unreflectConstructor Lookup.unreflectConstructor}
+ *     to convert a {@link Constructor} into a method handle.
+ * <li>By calling the factory method {@link Lookup#unreflectGetter Lookup.unreflectGetter}
+ *     or {@link Lookup#unreflectSetter Lookup.unreflectSetter}
+ *     to convert a {@link Field} into a method handle.
+ * </ul>
+ * In all of these cases, it is possible to crack the resulting direct method handle
+ * to recover a symbolic reference for the underlying method, constructor, or field.
+ * Cracking must be done via a {@code Lookup} object equivalent to that which created
+ * the target method handle, or which has enough access permissions to recreate
+ * an equivalent method handle.
  *
+ * <h1><a name="refkinds"></a>Reference kinds</h1>
+ * The <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>
+ * correspond to all major use cases for methods, constructors, and fields.
+ * These use cases may be distinguished using small integers as follows:
+ * <table border=1 cellpadding=5 summary="reference kinds">
+ * <tr><th>reference kind</th><th>descriptive name</th><th>scope</th><th>member</th><th>behavior</th></tr>
+ * <tr>
+ *     <td>{@code 1}</td><td>{@code REF_getField}</td><td>{@code class}</td>
+ *     <td>{@code FT f;}</td><td>{@code (T) this.f;}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 2}</td><td>{@code REF_getStatic}</td><td>{@code class} or {@code interface}</td>
+ *     <td>{@code static}<br>{@code FT f;}</td><td>{@code (T) C.f;}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 3}</td><td>{@code REF_putField}</td><td>{@code class}</td>
+ *     <td>{@code FT f;}</td><td>{@code this.f = x;}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 4}</td><td>{@code REF_putStatic}</td><td>{@code class}</td>
+ *     <td>{@code static}<br>{@code FT f;}</td><td>{@code C.f = arg;}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 5}</td><td>{@code REF_invokeVirtual}</td><td>{@code class}</td>
+ *     <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 6}</td><td>{@code REF_invokeStatic}</td><td>{@code class} or {@code interface}</td>
+ *     <td>{@code static}<br>{@code T m(A*);}</td><td>{@code (T) C.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 7}</td><td>{@code REF_invokeSpecial}</td><td>{@code class} or {@code interface}</td>
+ *     <td>{@code T m(A*);}</td><td>{@code (T) super.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 8}</td><td>{@code REF_newInvokeSpecial}</td><td>{@code class}</td>
+ *     <td>{@code C(A*);}</td><td>{@code new C(arg*);}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 9}</td><td>{@code REF_invokeInterface}</td><td>{@code interface}</td>
+ *     <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
-       REF_getField                = Constants.REF_getField,
-       REF_getStatic               = Constants.REF_getStatic,
-       REF_putField                = Constants.REF_putField,
-       REF_putStatic               = Constants.REF_putStatic,
-       REF_invokeVirtual           = Constants.REF_invokeVirtual,
-       REF_invokeStatic            = Constants.REF_invokeStatic,
-       REF_invokeSpecial           = Constants.REF_invokeSpecial,
-       REF_newInvokeSpecial        = Constants.REF_newInvokeSpecial,
-       REF_invokeInterface         = Constants.REF_invokeInterface;
+public
+interface MethodHandleInfo {
+    /**
+     * A direct method handle reference kind,
+     * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
+     */
+    public static final int
+        REF_getField                = Constants.REF_getField,
+        REF_getStatic               = Constants.REF_getStatic,
+        REF_putField                = Constants.REF_putField,
+        REF_putStatic               = Constants.REF_putStatic,
+        REF_invokeVirtual           = Constants.REF_invokeVirtual,
+        REF_invokeStatic            = Constants.REF_invokeStatic,
+        REF_invokeSpecial           = Constants.REF_invokeSpecial,
+        REF_newInvokeSpecial        = Constants.REF_newInvokeSpecial,
+        REF_invokeInterface         = Constants.REF_invokeInterface;
 
-   private final Class<?> declaringClass;
-   private final String name;
-   private final MethodType methodType;
-   private final int referenceKind;
+    /**
+     * Returns the reference kind of the cracked method handle, which in turn
+     * determines whether the method handle's underlying member was a constructor, method, or field.
+     * See the <a href="MethodHandleInfo.html#refkinds">table above</a> for definitions.
+     * @return the integer code for the kind of reference used to access the underlying member
+     */
+    public int getReferenceKind();
 
-   public MethodHandleInfo(MethodHandle mh) {
-       MemberName mn = mh.internalMemberName();
-       if (mn == null)  throw new IllegalArgumentException("not a direct method handle");
-       this.declaringClass = mn.getDeclaringClass();
-       this.name = mn.getName();
-       this.methodType = mn.getMethodOrFieldType();
-       byte refKind = mn.getReferenceKind();
-       if (refKind == REF_invokeSpecial && !mh.isInvokeSpecial())
-           // Devirtualized method invocation is usually formally virtual.
-           refKind = REF_invokeVirtual;
-       this.referenceKind = refKind;
-   }
+    /**
+     * Returns the class in which the cracked method handle's underlying member was defined.
+     * @return the declaring class of the underlying member
+     */
+    public Class<?> getDeclaringClass();
 
-   public Class<?> getDeclaringClass() {
-       return declaringClass;
-   }
+    /**
+     * Returns the name of the cracked method handle's underlying member.
+     * This is {@code "&lt;init&gt;"} if the underlying member was a constructor,
+     * else it is a simple method name or field name.
+     * @return the simple name of the underlying member
+     */
+    public String getName();
 
-   public String getName() {
-       return name;
-   }
+    /**
+     * Returns the nominal type of the cracked symbolic reference, expressed as a method type.
+     * If the reference is to a constructor, the return type will be {@code void}.
+     * If it is to a non-static method, the method type will not mention the {@code this} parameter.
+     * If it is to a field and the requested access is to read the field,
+     * the method type will have no parameters and return the field type.
+     * If it is to a field and the requested access is to write the field,
+     * the method type will have one parameter of the field type and return {@code void}.
+     * <p>
+     * Note that original direct method handle may include a leading {@code this} parameter,
+     * or (in the case of a constructor) will replace the {@code void} return type
+     * with the constructed class.
+     * The nominal type does not include any {@code this} parameter,
+     * and (in the case of a constructor) will return {@code void}.
+     * @return the type of the underlying member, expressed as a method type
+     */
+    public MethodType getMethodType();
 
-   public MethodType getMethodType() {
-       return methodType;
-   }
+    // Utility methods.
+    // NOTE: class/name/type and reference kind constitute a symbolic reference
+    // member and modifiers are an add-on, derived from Core Reflection (or the equivalent)
 
-   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 NullPointerException if either argument is {@code null}
+     * @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";
-            case REF_getStatic: return "getstatic";
-            case REF_putField: return "putfield";
-            case REF_putStatic: return "putstatic";
-            case REF_invokeVirtual: return "invokevirtual";
-            case REF_invokeStatic: return "invokestatic";
-            case REF_invokeSpecial: return "invokespecial";
-            case REF_newInvokeSpecial: return "newinvokespecial";
-            case REF_invokeInterface: return "invokeinterface";
-            default: return "UNKNOWN_REFENCE_KIND" + "[" + referenceKind + "]";
-        }
+    /**
+     * Determines if the underlying member was a variable arity method or constructor.
+     * Such members are represented by method handles that are varargs collectors.
+     * @implSpec
+     * This produces a result equivalent to:
+     * <pre>{@code
+     *     getReferenceKind() >= REF_invokeVirtual && Modifier.isTransient(getModifiers())
+     * }</pre>
+     *
+     *
+     * @return {@code true} if and only if the underlying member was declared with variable arity.
+     */
+    // spelling derived from java.lang.reflect.Executable, not MethodHandle.isVarargsCollector
+    public default boolean isVarArgs()  {
+        // fields are never varargs:
+        if (MethodHandleNatives.refKindIsField((byte) getReferenceKind()))
+            return false;
+        // not in the public API: Modifier.VARARGS
+        final int ACC_VARARGS = 0x00000080;  // from JVMS 4.6 (Table 4.20)
+        assert(ACC_VARARGS == Modifier.TRANSIENT);
+        return Modifier.isTransient(getModifiers());
     }
 
-    @Override
-    public String toString() {
-        return String.format("%s %s.%s:%s", getReferenceKindString(referenceKind),
-                             declaringClass.getName(), name, methodType);
+    /**
+     * 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 argument is not a valid
+     *            <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
+     */
+    public static String referenceKindToString(int referenceKind) {
+        if (!MethodHandleNatives.refKindIsValid(referenceKind))
+            throw newIllegalArgumentException("invalid reference kind", referenceKind);
+        return MethodHandleNatives.refKindName((byte)referenceKind);
+    }
+
+    /**
+     * Returns a string representation for a {@code MethodHandleInfo},
+     * given the four parts of its symbolic reference.
+     * This is defined to be of the form {@code "RK C.N:MT"}, where {@code RK} is the
+     * {@linkplain #referenceKindToString reference kind string} for {@code kind},
+     * {@code C} is the {@linkplain java.lang.Class#getName name} of {@code defc}
+     * {@code N} is the {@code name}, and
+     * {@code MT} is the {@code type}.
+     * These four values may be obtained from the
+     * {@linkplain #getReferenceKind reference kind},
+     * {@linkplain #getDeclaringClass declaring class},
+     * {@linkplain #getName member name},
+     * and {@linkplain #getMethodType method type}
+     * of a {@code MethodHandleInfo} object.
+     *
+     * @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 {@linkplain #getReferenceKind reference kind} part of the symbolic reference
+     * @param defc the {@linkplain #getDeclaringClass declaring class} part of the symbolic reference
+     * @param name the {@linkplain #getName member name} part of the symbolic reference
+     * @param type the {@linkplain #getMethodType method type} part of the symbolic reference
+     * @return a string of the form {@code "RK C.N:MT"}
+     * @exception IllegalArgumentException if the first argument is not a valid
+     *            <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
+     * @exception NullPointerException if any reference argument is {@code null}
+     */
+    public static String toString(int kind, Class<?> defc, String name, MethodType type) {
+        Objects.requireNonNull(name); Objects.requireNonNull(type);
+        return String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type);
     }
 }
--- a/src/share/classes/java/lang/invoke/MethodHandleNatives.java	Tue Sep 03 23:47:27 2013 -0400
+++ b/src/share/classes/java/lang/invoke/MethodHandleNatives.java	Tue Sep 03 21:42:56 2013 -0700
@@ -205,6 +205,9 @@
     static boolean refKindIsMethod(byte refKind) {
         return !refKindIsField(refKind) && (refKind != REF_newInvokeSpecial);
     }
+    static boolean refKindIsConstructor(byte refKind) {
+        return (refKind == REF_newInvokeSpecial);
+    }
     static boolean refKindHasReceiver(byte refKind) {
         assert(refKindIsValid(refKind));
         return (refKind & 1) != 0;
@@ -313,7 +316,65 @@
      * The method assumes the following arguments on the stack:
      * 0: the method handle being invoked
      * 1-N: the arguments to the method handle invocation
-     * N+1: an implicitly added type argument (the given MethodType)
+     * N+1: an optional, implicitly added argument (typically the given MethodType)
+     * <p>
+     * The nominal method at such a call site is an instance of
+     * a signature-polymorphic method (see @PolymorphicSignature).
+     * Such method instances are user-visible entities which are
+     * "split" from the generic placeholder method in {@code MethodHandle}.
+     * (Note that the placeholder method is not identical with any of
+     * its instances.  If invoked reflectively, is guaranteed to throw an
+     * {@code UnsupportedOperationException}.)
+     * If the signature-polymorphic method instance is ever reified,
+     * it appears as a "copy" of the original placeholder
+     * (a native final member of {@code MethodHandle}) except
+     * that its type descriptor has shape required by the instance,
+     * and the method instance is <em>not</em> varargs.
+     * The method instance is also marked synthetic, since the
+     * method (by definition) does not appear in Java source code.
+     * <p>
+     * The JVM is allowed to reify this method as instance metadata.
+     * For example, {@code invokeBasic} is always reified.
+     * But the JVM may instead call {@code linkMethod}.
+     * If the result is an * ordered pair of a {@code (method, appendix)},
+     * the method gets all the arguments (0..N inclusive)
+     * plus the appendix (N+1), and uses the appendix to complete the call.
+     * In this way, one reusable method (called a "linker method")
+     * can perform the function of any number of polymorphic instance
+     * methods.
+     * <p>
+     * Linker methods are allowed to be weakly typed, with any or
+     * all references rewritten to {@code Object} and any primitives
+     * (except {@code long}/{@code float}/{@code double})
+     * rewritten to {@code int}.
+     * A linker method is trusted to return a strongly typed result,
+     * according to the specific method type descriptor of the
+     * signature-polymorphic instance it is emulating.
+     * This can involve (as necessary) a dynamic check using
+     * data extracted from the appendix argument.
+     * <p>
+     * The JVM does not inspect the appendix, other than to pass
+     * it verbatim to the linker method at every call.
+     * This means that the JDK runtime has wide latitude
+     * for choosing the shape of each linker method and its
+     * corresponding appendix.
+     * Linker methods should be generated from {@code LambdaForm}s
+     * so that they do not become visible on stack traces.
+     * <p>
+     * The {@code linkMethod} call is free to omit the appendix
+     * (returning null) and instead emulate the required function
+     * completely in the linker method.
+     * As a corner case, if N==255, no appendix is possible.
+     * In this case, the method returned must be custom-generated to
+     * to perform any needed type checking.
+     * <p>
+     * If the JVM does not reify a method at a call site, but instead
+     * calls {@code linkMethod}, the corresponding call represented
+     * in the bytecodes may mention a valid method which is not
+     * representable with a {@code MemberName}.
+     * Therefore, use cases for {@code linkMethod} tend to correspond to
+     * special cases in reflective code such as {@code findVirtual}
+     * or {@code revealDirect}.
      */
     static MemberName linkMethod(Class<?> callerClass, int refKind,
                                  Class<?> defc, String name, Object type,
--- a/src/share/classes/java/lang/invoke/MethodHandles.java	Tue Sep 03 23:47:27 2013 -0400
+++ b/src/share/classes/java/lang/invoke/MethodHandles.java	Tue Sep 03 21:42:56 2013 -0700
@@ -26,8 +26,6 @@
 package java.lang.invoke;
 
 import java.lang.reflect.*;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -54,6 +52,7 @@
  * </ul>
  * <p>
  * @author John Rose, JSR 292 EG
+ * @since 1.7
  */
 public class MethodHandles {
 
@@ -97,6 +96,38 @@
     }
 
     /**
+     * Performs an unchecked "crack" of a direct method handle.
+     * The result is as if the user had obtained a lookup object capable enough
+     * 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#reflectAs MethodHandleInfo.reflectAs}
+     * to resolve the symbolic reference to a member.
+     * <p>
+     * If there is a security manager, its {@code checkPermission} method
+     * is called with a {@code ReflectPermission("suppressAccessChecks")} permission.
+     * @param <T> the desired type of the result, either {@link Member} or a subtype
+     * @param target a direct method handle to crack into symbolic reference components
+     * @param expected a class object representing the desired result type {@code T}
+     * @return a reference to the method, constructor, or field object
+     * @exception SecurityException if the caller is not privileged to call {@code setAccessible}
+     * @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
+    reflectAs(Class<T> expected, MethodHandle target) {
+        SecurityManager smgr = System.getSecurityManager();
+        if (smgr != null)  smgr.checkPermission(ACCESS_PERMISSION);
+        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 =
+        new ReflectPermission("suppressAccessChecks");
+
+    /**
      * A <em>lookup object</em> is a factory for creating method handles,
      * when the creation requires access checking.
      * Method handles do not perform
@@ -647,6 +678,7 @@
                 return invoker(type);
             if ("invokeExact".equals(name))
                 return exactInvoker(type);
+            assert(!MemberName.isMethodHandleInvokeName(name));
             return null;
         }
 
@@ -892,6 +924,10 @@
          * @throws NullPointerException if the argument is null
          */
         public MethodHandle unreflect(Method m) throws IllegalAccessException {
+            if (m.getDeclaringClass() == MethodHandle.class) {
+                MethodHandle mh = unreflectForMH(m);
+                if (mh != null)  return mh;
+            }
             MemberName method = new MemberName(m);
             byte refKind = method.getReferenceKind();
             if (refKind == REF_invokeSpecial)
@@ -900,6 +936,12 @@
             Lookup lookup = m.isAccessible() ? IMPL_LOOKUP : this;
             return lookup.getDirectMethod(refKind, method.getDeclaringClass(), method, findBoundCallerClass(method));
         }
+        private MethodHandle unreflectForMH(Method m) {
+            // these names require special lookups because they throw UnsupportedOperationException
+            if (MemberName.isMethodHandleInvokeName(m.getName()))
+                return MethodHandleImpl.fakeMethodHandleInvoke(new MemberName(m));
+            return null;
+        }
 
         /**
          * Produces a method handle for a reflected method.
@@ -1004,6 +1046,46 @@
             return unreflectField(f, true);
         }
 
+        /**
+         * Cracks a direct method handle created by this lookup object or a similar one.
+         * Security and access checks are performed to ensure that this lookup object
+         * is capable of reproducing the target method handle.
+         * This means that the cracking may fail if target is a direct method handle
+         * but was created by an unrelated lookup object.
+         * @param target a direct method handle to crack into symbolic reference components
+         * @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 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) {
+            MemberName member = target.internalMemberName();
+            if (member == null || (!member.isResolved() && !member.isMethodHandleInvoke()))
+                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,
+                // we encode this extra degree of freedom using MH.isInvokeSpecial.
+                refKind = REF_invokeVirtual;
+            if (refKind == REF_invokeVirtual && defc.isInterface())
+                // Symbolic reference is through interface but resolves to Object method (toString, etc.)
+                refKind = REF_invokeInterface;
+            // Check SM permissions and member access before cracking.
+            try {
+                checkSecurityManager(defc, member);
+                checkAccess(refKind, defc, member);
+            } catch (IllegalAccessException ex) {
+                throw new IllegalArgumentException(ex);
+            }
+            // Produce the handle to the results.
+            return new InfoFromMemberName(this, member, refKind);
+        }
+
         /// Helper methods, all package-private.
 
         MemberName resolveOrFail(byte refKind, Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
@@ -1201,12 +1283,12 @@
         private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,
                                                    boolean doRestrict, Class<?> callerClass) throws IllegalAccessException {
             checkMethod(refKind, refc, method);
-            if (method.isMethodHandleInvoke())
-                return fakeMethodHandleInvoke(method);
+            assert(!method.isMethodHandleInvoke());
 
             Class<?> refcAsSuper;
             if (refKind == REF_invokeSpecial &&
                 refc != lookupClass() &&
+                !refc.isInterface() &&
                 refc != (refcAsSuper = lookupClass().getSuperclass()) &&
                 refc.isAssignableFrom(lookupClass())) {
                 assert(!method.getName().equals("<init>"));  // not this code path
@@ -1234,9 +1316,6 @@
                 mh = restrictReceiver(method, mh, lookupClass());
             return mh;
         }
-        private MethodHandle fakeMethodHandleInvoke(MemberName method) {
-            return throwException(method.getReturnType(), UnsupportedOperationException.class);
-        }
         private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
                                              Class<?> callerClass)
                                              throws IllegalAccessException {
--- a/src/share/classes/java/lang/invoke/SerializedLambda.java	Tue Sep 03 23:47:27 2013 -0400
+++ b/src/share/classes/java/lang/invoke/SerializedLambda.java	Tue Sep 03 21:42:56 2013 -0700
@@ -225,7 +225,7 @@
 
     @Override
     public String toString() {
-        String implKind=MethodHandleInfo.getReferenceKindString(implMethodKind);
+        String implKind=MethodHandleInfo.referenceKindToString(implMethodKind);
         return String.format("SerializedLambda[%s=%s, %s=%s.%s:%s, " +
                              "%s=%s %s.%s:%s, %s=%s, %s=%d]",
                              "capturingClass", capturingClass,
--- a/test/java/lang/invoke/7087570/Test7087570.java	Tue Sep 03 23:47:27 2013 -0400
+++ b/test/java/lang/invoke/7087570/Test7087570.java	Tue Sep 03 21:42:56 2013 -0700
@@ -35,20 +35,9 @@
 
 import static java.lang.invoke.MethodHandles.*;
 import static java.lang.invoke.MethodType.*;
+import static java.lang.invoke.MethodHandleInfo.*;
 
 public class Test7087570 {
-    // XXX may remove the following constant declarations when MethodHandleInfo is made public
-    private static final int
-            REF_getField                = 1,
-            REF_getStatic               = 2,
-            REF_putField                = 3,
-            REF_putStatic               = 4,
-            REF_invokeVirtual           = 5,
-            REF_invokeStatic            = 6,
-            REF_invokeSpecial           = 7,
-            REF_newInvokeSpecial        = 8,
-            REF_invokeInterface         = 9,
-            REF_LIMIT                  = 10;
 
     private static final TestMethodData[] TESTS = new TestMethodData[] {
         // field accessors
@@ -87,17 +76,17 @@
     }
 
     private static void doTest(MethodHandle mh, TestMethodData testMethod) {
-        Object mhi = newMethodHandleInfo(mh);
+        MethodHandleInfo mhi = LOOKUP.revealDirect(mh);
 
         System.out.printf("%s.%s: %s, nominal refKind: %s, actual refKind: %s\n",
                           testMethod.clazz.getName(), testMethod.name, testMethod.methodType,
-                          REF_KIND_NAMES[testMethod.referenceKind],
-                          REF_KIND_NAMES[getReferenceKind(mhi)]);
-        assertEquals(testMethod.name,           getName(mhi));
-        assertEquals(testMethod.methodType,     getMethodType(mhi));
-        assertEquals(testMethod.declaringClass, getDeclaringClass(mhi));
+                          referenceKindToString(testMethod.referenceKind),
+                          referenceKindToString(mhi.getReferenceKind()));
+        assertEquals(testMethod.name,           mhi.getName());
+        assertEquals(testMethod.methodType,     mhi.getMethodType());
+        assertEquals(testMethod.declaringClass, mhi.getDeclaringClass());
         assertEquals(testMethod.referenceKind == REF_invokeSpecial, isInvokeSpecial(mh));
-        assertRefKindEquals(testMethod.referenceKind,  getReferenceKind(mhi));
+        assertRefKindEquals(testMethod.referenceKind,  mhi.getReferenceKind());
     }
 
     private static void testWithLookup() throws Throwable {
@@ -122,50 +111,8 @@
         return methodType(void.class, clazz);
     }
 
-    private static final String[] REF_KIND_NAMES = {
-        "MH::invokeBasic",
-        "REF_getField", "REF_getStatic", "REF_putField", "REF_putStatic",
-        "REF_invokeVirtual", "REF_invokeStatic", "REF_invokeSpecial",
-        "REF_newInvokeSpecial", "REF_invokeInterface"
-    };
-
     private static final Lookup LOOKUP = lookup();
 
-    // XXX may remove the following reflective logic when MethodHandleInfo is made public
-    private static final MethodHandle MH_IS_INVOKESPECIAL;
-    private static final MethodHandle MHI_CONSTRUCTOR;
-    private static final MethodHandle MHI_GET_NAME;
-    private static final MethodHandle MHI_GET_METHOD_TYPE;
-    private static final MethodHandle MHI_GET_DECLARING_CLASS;
-    private static final MethodHandle MHI_GET_REFERENCE_KIND;
-
-    static {
-        try {
-            // This is white box testing.  Use reflection to grab private implementation bits.
-            String magicName = "IMPL_LOOKUP";
-            Field magicLookup = MethodHandles.Lookup.class.getDeclaredField(magicName);
-            // This unit test will fail if a security manager is installed.
-            magicLookup.setAccessible(true);
-            // Forbidden fruit...
-            Lookup directInvokeLookup = (Lookup) magicLookup.get(null);
-            Class<?> mhiClass = Class.forName("java.lang.invoke.MethodHandleInfo", false, MethodHandle.class.getClassLoader());
-            MH_IS_INVOKESPECIAL = directInvokeLookup
-                    .findVirtual(MethodHandle.class, "isInvokeSpecial", methodType(boolean.class));
-            MHI_CONSTRUCTOR = directInvokeLookup
-                    .findConstructor(mhiClass, methodType(void.class, MethodHandle.class));
-            MHI_GET_NAME = directInvokeLookup
-                    .findVirtual(mhiClass, "getName", methodType(String.class));
-            MHI_GET_METHOD_TYPE = directInvokeLookup
-                    .findVirtual(mhiClass, "getMethodType", methodType(MethodType.class));
-            MHI_GET_DECLARING_CLASS = directInvokeLookup
-                    .findVirtual(mhiClass, "getDeclaringClass", methodType(Class.class));
-            MHI_GET_REFERENCE_KIND = directInvokeLookup
-                    .findVirtual(mhiClass, "getReferenceKind", methodType(int.class));
-        } catch (ReflectiveOperationException ex) {
-            throw new Error(ex);
-        }
-    }
-
     private static class TestMethodData {
         final Class<?> clazz;
         final String name;
@@ -208,7 +155,9 @@
             return LOOKUP.findStatic(testMethod.clazz, testMethod.name, testMethod.methodType);
         case REF_invokeSpecial:
             Class<?> thisClass = LOOKUP.lookupClass();
-            return LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
+            MethodHandle smh = LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
+            noteInvokeSpecial(smh);
+            return smh;
         case REF_newInvokeSpecial:
             return LOOKUP.findConstructor(testMethod.clazz, testMethod.methodType);
         default:
@@ -238,7 +187,9 @@
         case REF_invokeSpecial: {
                 Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
                 Class<?> thisClass = LOOKUP.lookupClass();
-                return LOOKUP.unreflectSpecial(m, thisClass);
+                MethodHandle smh = LOOKUP.unreflectSpecial(m, thisClass);
+                noteInvokeSpecial(smh);
+                return smh;
             }
         case REF_newInvokeSpecial: {
                 Constructor c = testMethod.clazz.getDeclaredConstructor(testMethod.methodType.parameterArray());
@@ -249,59 +200,20 @@
         }
     }
 
-    private static Object newMethodHandleInfo(MethodHandle mh) {
-        try {
-            return MHI_CONSTRUCTOR.invoke(mh);
-        } catch (Throwable ex) {
-            throw new Error(ex);
-        }
+    private static List<MethodHandle> specialMethodHandles = new ArrayList<>();
+    private static void noteInvokeSpecial(MethodHandle mh) {
+        specialMethodHandles.add(mh);
+        assert(isInvokeSpecial(mh));
     }
-
     private static boolean isInvokeSpecial(MethodHandle mh) {
-        try {
-            return (boolean) MH_IS_INVOKESPECIAL.invokeExact(mh);
-        } catch (Throwable ex) {
-            throw new Error(ex);
-        }
-    }
-
-    private static String getName(Object mhi) {
-        try {
-            return (String) MHI_GET_NAME.invoke(mhi);
-        } catch (Throwable ex) {
-            throw new Error(ex);
-        }
-    }
-
-    private static MethodType getMethodType(Object mhi) {
-        try {
-            return (MethodType) MHI_GET_METHOD_TYPE.invoke(mhi);
-        } catch (Throwable ex) {
-            throw new Error(ex);
-        }
-    }
-
-    private static Class<?> getDeclaringClass(Object mhi) {
-        try {
-            return (Class<?>) MHI_GET_DECLARING_CLASS.invoke(mhi);
-        } catch (Throwable ex) {
-            throw new Error(ex);
-        }
-    }
-
-    private static int getReferenceKind(Object mhi) {
-        try {
-            return (int) MHI_GET_REFERENCE_KIND.invoke(mhi);
-        } catch (Throwable ex) {
-            throw new Error(ex);
-        }
+        return specialMethodHandles.contains(mh);
     }
 
     private static void assertRefKindEquals(int expect, int observed) {
         if (expect == observed) return;
 
-        String msg = "expected " + REF_KIND_NAMES[(int) expect] +
-                     " but observed " + REF_KIND_NAMES[(int) observed];
+        String msg = "expected " + referenceKindToString(expect) +
+                     " but observed " + referenceKindToString(observed);
         System.out.println("FAILED: " + msg);
         throw new AssertionError(msg);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/RevealDirectTest.java	Tue Sep 03 21:42:56 2013 -0700
@@ -0,0 +1,753 @@
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+/*
+ * @test
+ * @summary verify Lookup.revealDirect on a variety of input handles
+ * @compile -XDignore.symbol.file RevealDirectTest.java
+ * @run junit/othervm -ea -esa test.java.lang.invoke.RevealDirectTest
+ *
+ * @test
+ * @summary verify Lookup.revealDirect on a variety of input handles, with security manager
+ * @run main/othervm/policy=jtreg.security.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.RevealDirectTest
+ */
+
+/* To run manually:
+ * $ $JAVA8X_HOME/bin/javac -cp $JUNIT4_JAR -d ../../../.. -XDignore.symbol.file RevealDirectTest.java
+ * $ $JAVA8X_HOME/bin/java  -cp $JUNIT4_JAR:../../../.. -ea -esa org.junit.runner.JUnitCore test.java.lang.invoke.RevealDirectTest
+ * $ $JAVA8X_HOME/bin/java  -cp $JUNIT4_JAR:../../../.. -ea -esa    -Djava.security.manager test.java.lang.invoke.RevealDirectTest
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.reflect.*;
+import java.lang.invoke.*;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+import static java.lang.invoke.MethodHandleInfo.*;
+import java.util.*;
+import static org.junit.Assert.*;
+import org.junit.*;
+
+public class RevealDirectTest {
+    public static void main(String... av) throws Throwable {
+        // Run the @Test methods explicitly, in case we don't want to use the JUnitCore driver.
+        // This appears to be necessary when running with a security manager.
+        Throwable fail = null;
+        for (Method test : RevealDirectTest.class.getDeclaredMethods()) {
+            if (!test.isAnnotationPresent(Test.class))  continue;
+            try {
+                test.invoke(new RevealDirectTest());
+            } catch (Throwable ex) {
+                if (ex instanceof InvocationTargetException)
+                    ex = ex.getCause();
+                if (fail == null)  fail = ex;
+                System.out.println("Testcase: "+test.getName()
+                                   +"("+test.getDeclaringClass().getName()
+                                   +"):\tCaused an ERROR");
+                System.out.println(ex);
+                ex.printStackTrace(System.out);
+            }
+        }
+        if (fail != null)  throw fail;
+    }
+
+    public interface SimpleSuperInterface {
+        public abstract int getInt();
+        public static void printAll(String... args) {
+            System.out.println(Arrays.toString(args));
+        }
+        public int NICE_CONSTANT = 42;
+    }
+    public interface SimpleInterface extends SimpleSuperInterface {
+        default float getFloat() { return getInt(); }
+        public static void printAll(String[] args) {
+            System.out.println(Arrays.toString(args));
+        }
+    }
+    public static class Simple implements SimpleInterface, Cloneable {
+        public int intField;
+        public final int finalField;
+        private static String stringField;
+        public int getInt() { return NICE_CONSTANT; }
+        private static Number getNum() { return 804; }
+        public Simple clone() {
+            try {
+                return (Simple) super.clone();
+            } catch (CloneNotSupportedException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+        Simple() { finalField = -NICE_CONSTANT; }
+        private static Lookup localLookup() { return lookup(); }
+        private static List<Member> members() { return getMembers(lookup().lookupClass()); };
+    }
+
+    static boolean VERBOSE = false;
+
+    @Test public void testSimple() throws Throwable {
+        if (VERBOSE)  System.out.println("@Test testSimple");
+        testOnMembers("testSimple", Simple.members(), Simple.localLookup());
+    }
+    @Test public void testPublicLookup() throws Throwable {
+        if (VERBOSE)  System.out.println("@Test testPublicLookup");
+        List<Member> mems = publicOnly(Simple.members());
+        Lookup pubLookup = publicLookup(), privLookup = Simple.localLookup();
+        testOnMembers("testPublicLookup/1", mems, pubLookup);
+        // reveal using publicLookup:
+        testOnMembers("testPublicLookup/2", mems, privLookup, pubLookup);
+        // lookup using publicLookup, but reveal using private:
+        testOnMembers("testPublicLookup/3", mems, pubLookup, privLookup);
+    }
+    @Test public void testPublicLookupNegative() throws Throwable {
+        if (VERBOSE)  System.out.println("@Test testPublicLookupNegative");
+        List<Member> mems = nonPublicOnly(Simple.members());
+        Lookup pubLookup = publicLookup(), privLookup = Simple.localLookup();
+        testOnMembersNoLookup("testPublicLookupNegative/1", mems, pubLookup);
+        testOnMembersNoReveal("testPublicLookupNegative/2", mems, privLookup, pubLookup);
+        testOnMembersNoReflect("testPublicLookupNegative/3", mems, privLookup, pubLookup);
+    }
+    @Test public void testJavaLangClass() throws Throwable {
+        if (VERBOSE)  System.out.println("@Test testJavaLangClass");
+        List<Member> mems = callerSensitive(false, publicOnly(getMembers(Class.class)));
+        mems = limit(20, mems);
+        testOnMembers("testJavaLangClass", mems, Simple.localLookup());
+    }
+    @Test public void testCallerSensitive() throws Throwable {
+        if (VERBOSE)  System.out.println("@Test testCallerSensitive");
+        List<Member> mems = union(getMembers(MethodHandles.class, "lookup"),
+                                  getMembers(Method.class, "invoke"),
+                                  getMembers(Field.class, "get", "set", "getLong"),
+                                  getMembers(Class.class));
+        mems = callerSensitive(true, publicOnly(mems));
+        mems = limit(10, mems);
+        testOnMembers("testCallerSensitive", mems, Simple.localLookup());
+    }
+    @Test public void testCallerSensitiveNegative() throws Throwable {
+        if (VERBOSE)  System.out.println("@Test testCallerSensitiveNegative");
+        List<Member> mems = union(getMembers(MethodHandles.class, "lookup"),
+                                  getMembers(Class.class, "forName"),
+                                  getMembers(Method.class, "invoke"));
+        mems = callerSensitive(true, publicOnly(mems));
+        // CS methods cannot be looked up with publicLookup
+        testOnMembersNoLookup("testCallerSensitiveNegative", mems, publicLookup());
+    }
+    @Test public void testMethodHandleNatives() throws Throwable {
+        if (VERBOSE)  System.out.println("@Test testMethodHandleNatives");
+        List<Member> mems = getMembers(MethodHandle.class, "invoke", "invokeExact");
+        testOnMembers("testMethodHandleNatives", mems, Simple.localLookup());
+    }
+    @Test public void testMethodHandleInvokes() throws Throwable {
+        if (VERBOSE)  System.out.println("@Test testMethodHandleInvokes");
+        List<MethodType> types = new ArrayList<>();
+        Class<?>[] someParamTypes = { void.class, int.class, Object.class, Object[].class };
+        for (Class<?> rt : someParamTypes) {
+            for (Class<?> p0 : someParamTypes) {
+                if (p0 == void.class) { types.add(methodType(rt)); continue; }
+                for (Class<?> p1 : someParamTypes) {
+                    if (p1 == void.class) { types.add(methodType(rt, p0)); continue; }
+                    for (Class<?> p2 : someParamTypes) {
+                        if (p2 == void.class) { types.add(methodType(rt, p0, p1)); continue; }
+                        types.add(methodType(rt, p0, p1, p2));
+                    }
+                }
+            }
+        }
+        List<Member> mems = union(getPolyMembers(MethodHandle.class, "invoke", types),
+                                  getPolyMembers(MethodHandle.class, "invokeExact", types));
+        testOnMembers("testMethodHandleInvokes/1", mems, Simple.localLookup());
+        testOnMembers("testMethodHandleInvokes/2", mems, publicLookup());
+    }
+
+    static List<Member> getPolyMembers(Class<?> cls, String name, List<MethodType> types) {
+        assert(cls == MethodHandle.class);
+        ArrayList<Member> mems = new ArrayList<>();
+        for (MethodType type : types) {
+            mems.add(new SignaturePolymorphicMethod(name, type));
+        }
+        return mems;
+    }
+    static List<Member> getMembers(Class<?> cls) {
+        return getMembers(cls, (String[]) null);
+    }
+    static List<Member> getMembers(Class<?> cls, String... onlyNames) {
+        List<String> names = (onlyNames == null || onlyNames.length == 0 ? null : Arrays.asList(onlyNames));
+        ArrayList<Member> res = new ArrayList<>();
+        for (Class<?> sup : getSupers(cls)) {
+            res.addAll(getDeclaredMembers(sup, "getDeclaredFields"));
+            res.addAll(getDeclaredMembers(sup, "getDeclaredMethods"));
+            res.addAll(getDeclaredMembers(sup, "getDeclaredConstructors"));
+        }
+        res = new ArrayList<>(new LinkedHashSet<>(res));
+        for (int i = 0; i < res.size(); i++) {
+            Member mem = res.get(i);
+            if (!canBeReached(mem, cls) ||
+                res.indexOf(mem) != i ||
+                mem.isSynthetic() ||
+                (names != null && !names.contains(mem.getName()))
+                ) {
+                res.remove(i--);
+            }
+        }
+        return res;
+    }
+    static List<Class<?>> getSupers(Class<?> cls) {
+        ArrayList<Class<?>> res = new ArrayList<>();
+        ArrayList<Class<?>> intfs = new ArrayList<>();
+        for (Class<?> sup = cls; sup != null; sup = sup.getSuperclass()) {
+            res.add(sup);
+            for (Class<?> intf : cls.getInterfaces()) {
+                if (!intfs.contains(intf))
+                    intfs.add(intf);
+            }
+        }
+        for (int i = 0; i < intfs.size(); i++) {
+            for (Class<?> intf : intfs.get(i).getInterfaces()) {
+                if (!intfs.contains(intf))
+                    intfs.add(intf);
+            }
+        }
+        res.addAll(intfs);
+        //System.out.println("getSupers => "+res);
+        return res;
+    }
+    static boolean hasSM() {
+        return (System.getSecurityManager() != null);
+    }
+    static List<Member> getDeclaredMembers(Class<?> cls, String accessor) {
+        Member[] mems = {};
+        Method getter = getMethod(Class.class, accessor);
+        if (hasSM()) {
+            try {
+                mems = (Member[]) invokeMethod(getter, cls);
+            } catch (SecurityException ex) {
+                //if (VERBOSE)  ex.printStackTrace();
+                accessor = accessor.replace("Declared", "");
+                getter = getMethod(Class.class, accessor);
+                if (VERBOSE)  System.out.println("replaced accessor: "+getter);
+            }
+        }
+        if (mems.length == 0) {
+            try {
+                mems = (Member[]) invokeMethod(getter, cls);
+            } catch (SecurityException ex) {
+                ex.printStackTrace();
+            }
+        }
+        if (VERBOSE)  System.out.println(accessor+" "+cls.getName()+" => "+mems.length+" members");
+        return Arrays.asList(mems);
+    }
+    static Method getMethod(Class<?> cls, String name) {
+        try {
+            return cls.getMethod(name);
+        } catch (ReflectiveOperationException ex) {
+            throw new AssertionError(ex);
+        }
+    }
+    static Object invokeMethod(Method m, Object recv, Object... args) {
+        try {
+            return m.invoke(recv, args);
+        } catch (InvocationTargetException ex) {
+            Throwable ex2 = ex.getCause();
+            if (ex2 instanceof RuntimeException)  throw (RuntimeException) ex2;
+            if (ex2 instanceof Error)  throw (Error) ex2;
+            throw new AssertionError(ex);
+        } catch (ReflectiveOperationException ex) {
+            throw new AssertionError(ex);
+        }
+    }
+
+    static List<Member> limit(int len, List<Member> mems) {
+        if (mems.size() <= len)  return mems;
+        return mems.subList(0, len);
+    }
+    @SafeVarargs
+    static List<Member> union(List<Member> mems, List<Member>... mem2s) {
+        for (List<Member> mem2 : mem2s) {
+            for (Member m : mem2) {
+                if (!mems.contains(m))
+                    mems.add(m);
+            }
+        }
+        return mems;
+    }
+    static List<Member> callerSensitive(boolean cond, List<Member> members) {
+        for (Iterator<Member> i = members.iterator(); i.hasNext(); ) {
+            Member mem = i.next();
+            if (isCallerSensitive(mem) != cond)
+                i.remove();
+        }
+        if (members.isEmpty())  throw new AssertionError("trivial result");
+        return members;
+    }
+    static boolean isCallerSensitive(Member mem) {
+        if (!(mem instanceof AnnotatedElement))  return false;
+        AnnotatedElement ae = (AnnotatedElement) mem;
+        if (CS_CLASS != null)
+            return ae.isAnnotationPresent(sun.reflect.CallerSensitive.class);
+        for (java.lang.annotation.Annotation a : ae.getDeclaredAnnotations()) {
+            if (a.toString().contains(".CallerSensitive"))
+                return true;
+        }
+        return false;
+    }
+    static final Class<?> CS_CLASS;
+    static {
+        Class<?> c = null;
+        try {
+            c = sun.reflect.CallerSensitive.class;
+        } catch (SecurityException | LinkageError ex) {
+        }
+        CS_CLASS = c;
+    }
+    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();
+            if ((publicMods & mods) != 0 &&
+                (publicMods & mem.getDeclaringClass().getModifiers()) == 0)
+                mods -= publicMods;
+            if ((mods & mask) == (bits & mask))
+                i.remove();
+        }
+        return members;
+    }
+
+    void testOnMembers(String tname, List<Member> mems, Lookup lookup, Lookup... lookups) throws Throwable {
+        if (VERBOSE)  System.out.println("testOnMembers "+mems);
+        Lookup revLookup = (lookups.length > 0) ? lookups[0] : null;
+        if (revLookup == null)  revLookup = lookup;
+        Lookup refLookup = (lookups.length > 1) ? lookups[1] : null;
+        if (refLookup == null)  refLookup = lookup;
+        assert(lookups.length <= 2);
+        testOnMembersImpl(tname, mems, lookup, revLookup, refLookup, NO_FAIL);
+    }
+    void testOnMembersNoLookup(String tname, List<Member> mems, Lookup lookup) throws Throwable {
+        if (VERBOSE)  System.out.println("testOnMembersNoLookup "+mems);
+        testOnMembersImpl(tname, mems, lookup, null, null, FAIL_LOOKUP);
+    }
+    void testOnMembersNoReveal(String tname, List<Member> mems,
+                               Lookup lookup, Lookup negLookup) throws Throwable {
+        if (VERBOSE)  System.out.println("testOnMembersNoReveal "+mems);
+        testOnMembersImpl(tname, mems, lookup, negLookup, null, FAIL_REVEAL);
+    }
+    void testOnMembersNoReflect(String tname, List<Member> mems,
+                                Lookup lookup, Lookup negLookup) throws Throwable {
+        if (VERBOSE)  System.out.println("testOnMembersNoReflect "+mems);
+        testOnMembersImpl(tname, mems, lookup, lookup, negLookup, FAIL_REFLECT);
+    }
+    void testOnMembersImpl(String tname, List<Member> mems,
+                           Lookup lookup,
+                           Lookup revLookup,
+                           Lookup refLookup,
+                           int failureMode) throws Throwable {
+        Throwable fail = null;
+        int failCount = 0;
+        failureModeCounts = new int[FAIL_MODE_COUNT];
+        long tm0 = System.currentTimeMillis();
+        for (Member mem : mems) {
+            try {
+                testWithMember(mem, lookup, revLookup, refLookup, failureMode);
+            } catch (Throwable ex) {
+                if (fail == null)  fail = ex;
+                if (++failCount > 10) { System.out.println("*** FAIL: too many failures"); break; }
+                System.out.println("*** FAIL: "+mem+" => "+ex);
+                if (VERBOSE)  ex.printStackTrace(System.out);
+            }
+        }
+        long tm1 = System.currentTimeMillis();
+        System.out.printf("@Test %s executed %s tests in %d ms",
+                          tname, testKinds(failureModeCounts), (tm1-tm0)).println();
+        if (fail != null)  throw fail;
+    }
+    static String testKinds(int[] modes) {
+        int pos = modes[0], neg = -pos;
+        for (int n : modes)  neg += n;
+        if (neg == 0)  return pos + " positive";
+        String negs = "";
+        for (int n : modes)  negs += "/"+n;
+        negs = negs.replaceFirst("/"+pos+"/", "");
+        negs += " negative";
+        if (pos == 0)  return negs;
+        return pos + " positive, " + negs;
+    }
+    static class SignaturePolymorphicMethod implements Member {  // non-reflected instance of MH.invoke*
+        final String name;
+        final MethodType type;
+        SignaturePolymorphicMethod(String name, MethodType type) {
+            this.name = name;
+            this.type = type;
+        }
+        public String toString() {
+            String typeStr = type.toString();
+            if (isVarArgs())  typeStr = typeStr.replaceFirst("\\[\\])$", "...)");
+            return (Modifier.toString(getModifiers())
+                    +typeStr.substring(0, typeStr.indexOf('('))+" "
+                    +getDeclaringClass().getTypeName()+"."
+                    +getName()+typeStr.substring(typeStr.indexOf('(')));
+        }
+        public boolean equals(Object x) {
+            return (x instanceof SignaturePolymorphicMethod && equals((SignaturePolymorphicMethod)x));
+        }
+        public boolean equals(SignaturePolymorphicMethod that) {
+            return this.name.equals(that.name) && this.type.equals(that.type);
+        }
+        public int hashCode() {
+            return name.hashCode() * 31 + type.hashCode();
+        }
+        public Class<?> getDeclaringClass() { return MethodHandle.class; }
+        public String getName() { return name; }
+        public MethodType getMethodType() { return type; }
+        public int getModifiers() { return Modifier.PUBLIC | Modifier.FINAL | Modifier.NATIVE | SYNTHETIC; }
+        public boolean isVarArgs() { return Modifier.isTransient(getModifiers()); }
+        public boolean isSynthetic() { return true; }
+        public Class<?> getReturnType() { return type.returnType(); }
+        public Class<?>[] getParameterTypes() { return type.parameterArray(); }
+        static final int SYNTHETIC = 0x00001000;
+    }
+    static class UnreflectResult {  // a tuple
+        final MethodHandle mh;
+        final Throwable 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(Throwable 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 toInfoString()+"/v"+var;
+        }
+        public String toInfoString() {
+            return String.format("%s %s.%s:%s", MethodHandleInfo.referenceKindToString(kind),
+                                 mem.getDeclaringClass().getName(), 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);
+            } else if (mem instanceof SignaturePolymorphicMethod) {
+                return ((SignaturePolymorphicMethod)mem).getMethodType();
+            }
+            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) {
+        byte[] refKind = {0};
+        try {
+            return unreflectMemberOrThrow(lookup, mem, variation, refKind);
+        } catch (ReflectiveOperationException|SecurityException 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();
+        int      mods = mem.getModifiers();
+        boolean isStatic = Modifier.isStatic(mods);
+        MethodHandle mh = null;
+        byte kind = 0;
+        if (mem instanceof Method) {
+            Method m = (Method) mem;
+            MethodType type = methodType(m.getReturnType(), m.getParameterTypes());
+            boolean canBeSpecial = (!isStatic &&
+                                    (lookup.lookupModes() & Modifier.PRIVATE) != 0 &&
+                                    defc.isAssignableFrom(cls) &&
+                                    (!defc.isInterface() || Arrays.asList(cls.getInterfaces()).contains(defc)));
+            if (variation >= 2)
+                kind = REF_invokeSpecial;
+            else if (isStatic)
+                kind = REF_invokeStatic;
+            else if (defc.isInterface())
+                kind = REF_invokeInterface;
+            else
+                kind = REF_invokeVirtual;
+            refKind[0] = kind;
+            switch (variation) {
+            case 0:
+                mh = lookup.unreflect(m);
+                break;
+            case 1:
+                if (defc == MethodHandle.class &&
+                    !isStatic &&
+                    m.isVarArgs() &&
+                    Modifier.isFinal(mods) &&
+                    Modifier.isNative(mods)) {
+                    break;
+                }
+                if (isStatic)
+                    mh = lookup.findStatic(defc, name, type);
+                else
+                    mh = lookup.findVirtual(defc, name, type);
+                break;
+            case 2:
+                if (!canBeSpecial)
+                    break;
+                mh = lookup.unreflectSpecial(m, lookup.lookupClass());
+                break;
+            case 3:
+                if (!canBeSpecial)
+                    break;
+                mh = lookup.findSpecial(defc, name, type, lookup.lookupClass());
+                break;
+            }
+        } else if (mem instanceof SignaturePolymorphicMethod) {
+            SignaturePolymorphicMethod m = (SignaturePolymorphicMethod) mem;
+            MethodType type = methodType(m.getReturnType(), m.getParameterTypes());
+            kind = REF_invokeVirtual;
+            refKind[0] = kind;
+            switch (variation) {
+            case 0:
+                mh = lookup.findVirtual(defc, name, type);
+                break;
+            }
+        } else if (mem instanceof Constructor) {
+            name = "<init>";  // not used
+            Constructor<?> m = (Constructor<?>) mem;
+            MethodType type = methodType(void.class, m.getParameterTypes());
+            kind = REF_newInvokeSpecial;
+            refKind[0] = kind;
+            switch (variation) {
+            case 0:
+                mh = lookup.unreflectConstructor(m);
+                break;
+            case 1:
+                mh = lookup.findConstructor(defc, type);
+                break;
+            }
+        } else if (mem instanceof Field) {
+            Field m = (Field) mem;
+            Class<?> type = m.getType();
+            boolean canHaveSetter = !Modifier.isFinal(mods);
+            if (variation >= 2)
+                kind = (byte)(isStatic ? REF_putStatic : REF_putField);
+            else
+                kind = (byte)(isStatic ? REF_getStatic : REF_getField);
+            refKind[0] = kind;
+            switch (variation) {
+            case 0:
+                mh = lookup.unreflectGetter(m);
+                break;
+            case 1:
+                if (isStatic)
+                    mh = lookup.findStaticGetter(defc, name, type);
+                else
+                    mh = lookup.findGetter(defc, name, type);
+                break;
+            case 3:
+                if (!canHaveSetter)
+                    break;
+                mh = lookup.unreflectSetter(m);
+                break;
+            case 2:
+                if (!canHaveSetter)
+                    break;
+                if (isStatic)
+                    mh = lookup.findStaticSetter(defc, name, type);
+                else
+                    mh = lookup.findSetter(defc, name, type);
+                break;
+            }
+        } else {
+            throw new IllegalArgumentException(String.valueOf(mem));
+        }
+        if (mh == null)
+            // ran out of valid variations; return null to caller
+            return null;
+        return new UnreflectResult(mh, kind, mem, variation);
+    }
+    static boolean canBeReached(Member mem, Class<?> cls) {
+        Class<?> defc = mem.getDeclaringClass();
+        String   name = mem.getName();
+        int      mods = mem.getModifiers();
+        if (mem instanceof Constructor) {
+            name = "<init>";  // according to 292 spec.
+        }
+        if (defc == cls)
+            return true;
+        if (name.startsWith("<"))
+            return false;  // only my own constructors
+        if (Modifier.isPrivate(mods))
+            return false;  // only my own constructors
+        if (defc.getPackage() == cls.getPackage())
+            return true;   // package access or greater OK
+        if (Modifier.isPublic(mods))
+            return true;   // publics always OK
+        if (Modifier.isProtected(mods) && defc.isAssignableFrom(cls))
+            return true;   // protected OK
+        return false;
+    }
+    static boolean consistent(UnreflectResult res, MethodHandleInfo info) {
+        assert(res.mh != null);
+        assertEquals(res.kind, info.getReferenceKind());
+        assertEquals(res.mem.getModifiers(), info.getModifiers());
+        assertEquals(res.mem.getDeclaringClass(), info.getDeclaringClass());
+        String expectName = res.mem.getName();
+        if (res.kind == REF_newInvokeSpecial)
+            expectName = "<init>";
+        assertEquals(expectName, info.getName());
+        MethodType expectType = res.mh.type();
+        if ((res.kind & 1) == (REF_getField & 1))
+            expectType = expectType.dropParameterTypes(0, 1);
+        if (res.kind == REF_newInvokeSpecial)
+            expectType = expectType.changeReturnType(void.class);
+        assertEquals(expectType, info.getMethodType());
+        assertEquals(res.mh.isVarargsCollector(), isVarArgs(info));
+        assertEquals(res.toInfoString(), info.toString());
+        assertEquals(res.toInfoString(), MethodHandleInfo.toString(info.getReferenceKind(), info.getDeclaringClass(), info.getName(), info.getMethodType()));
+        return true;
+    }
+    static boolean isVarArgs(MethodHandleInfo info) {
+        return info.isVarArgs();
+    }
+    static boolean consistent(Member mem, Member mem2) {
+        assertEquals(mem, mem2);
+        return true;
+    }
+    static boolean consistent(MethodHandleInfo info, MethodHandleInfo info2) {
+        assertEquals(info.getReferenceKind(), info2.getReferenceKind());
+        assertEquals(info.getModifiers(), info2.getModifiers());
+        assertEquals(info.getDeclaringClass(), info2.getDeclaringClass());
+        assertEquals(info.getName(), info2.getName());
+        assertEquals(info.getMethodType(), info2.getMethodType());
+        assertEquals(isVarArgs(info), isVarArgs(info));
+        return true;
+    }
+    static boolean consistent(MethodHandle mh, MethodHandle mh2) {
+        assertEquals(mh.type(), mh2.type());
+        assertEquals(mh.isVarargsCollector(), mh2.isVarargsCollector());
+        return true;
+    }
+    int[] failureModeCounts;
+    static final int NO_FAIL=0, FAIL_LOOKUP=1, FAIL_REVEAL=2, FAIL_REFLECT=3, FAIL_MODE_COUNT=4;
+    void testWithMember(Member mem,
+                        Lookup lookup,      // initial lookup of member => MH
+                        Lookup revLookup,   // reveal MH => info
+                        Lookup refLookup,   // reflect info => member
+                        int failureMode) throws Throwable {
+        boolean expectEx1 = (failureMode == FAIL_LOOKUP);   // testOnMembersNoLookup
+        boolean expectEx2 = (failureMode == FAIL_REVEAL);   // testOnMembersNoReveal
+        boolean expectEx3 = (failureMode == FAIL_REFLECT);  // testOnMembersNoReflect
+        for (int variation = 0; ; variation++) {
+            UnreflectResult res = unreflectMember(lookup, mem, variation);
+            failureModeCounts[failureMode] += 1;
+            if (variation == 0)  assert(res != null);
+            if (res == null)  break;
+            if (VERBOSE && variation == 0)
+                System.out.println("from "+mem.getDeclaringClass().getSimpleName());
+            MethodHandle mh = res.mh;
+            Throwable   ex1 = res.ex;
+            if (VERBOSE)  System.out.println("  "+variation+": "+res+"  << "+(mh != null ? mh : ex1));
+            if (expectEx1 && ex1 != null)
+                continue;  // this is OK; we expected that lookup to fail
+            if (expectEx1)
+                throw new AssertionError("unexpected lookup for negative test");
+            if (ex1 != null && !expectEx1) {
+                if (failureMode != NO_FAIL)
+                    throw new AssertionError("unexpected lookup failure for negative test", ex1);
+                throw ex1;
+            }
+            MethodHandleInfo info;
+            try {
+                info = revLookup.revealDirect(mh);
+                if (expectEx2)  throw new AssertionError("unexpected revelation for negative test");
+            } catch (Throwable ex2) {
+                if (VERBOSE)  System.out.println("  "+variation+": "+res+" => "+mh.getClass().getName()+" => (EX2)"+ex2);
+                if (expectEx2)
+                    continue;  // this is OK; we expected the reflect to fail
+                if (failureMode != NO_FAIL)
+                    throw new AssertionError("unexpected revelation failure for negative test", ex2);
+                throw ex2;
+            }
+            assert(consistent(res, info));
+            Member mem2;
+            try {
+                mem2 = info.reflectAs(Member.class, refLookup);
+                if (expectEx3)  throw new AssertionError("unexpected reflection for negative test");
+                assert(!(mem instanceof SignaturePolymorphicMethod));
+            } catch (IllegalArgumentException ex3) {
+                if (VERBOSE)  System.out.println("  "+variation+": "+info+" => (EX3)"+ex3);
+                if (expectEx3)
+                    continue;  // this is OK; we expected the reflect to fail
+                if (mem instanceof SignaturePolymorphicMethod)
+                    continue;  // this is OK; we cannot reflect MH.invokeExact(a,b,c)
+                if (failureMode != NO_FAIL)
+                    throw new AssertionError("unexpected reflection failure for negative test", ex3);
+                throw ex3;
+            }
+            assert(consistent(mem, mem2));
+            UnreflectResult res2 = unreflectMember(lookup, mem2, variation);
+            MethodHandle mh2 = res2.mh;
+            assert(consistent(mh, mh2));
+            MethodHandleInfo info2 = lookup.revealDirect(mh2);
+            assert(consistent(info, info2));
+            assert(consistent(res, info2));
+            Member mem3;
+            if (hasSM())
+                mem3 = info2.reflectAs(Member.class, lookup);
+            else
+                mem3 = MethodHandles.reflectAs(Member.class, mh2);
+            assert(consistent(mem2, mem3));
+            if (hasSM()) {
+                try {
+                    MethodHandles.reflectAs(Member.class, mh2);
+                    throw new AssertionError("failed to throw on "+mem3);
+                } catch (SecurityException ex3) {
+                    // OK...
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/jtreg.security.policy	Tue Sep 03 21:42:56 2013 -0700
@@ -0,0 +1,9 @@
+/*
+ * security policy used by the test process
+ * must allow file reads so that jtreg itself can run
+ */
+
+grant {
+  // standard test activation permissions
+  permission java.io.FilePermission "*", "read";
+};