changeset 442:23f2de08d38b

meth-info-8008688.patch: second cut
author jrose
date Thu, 02 May 2013 19:25:01 -0700
parents 379345d2d26a
children eabaf978d9ee
files meth-info-8008688.patch
diffstat 1 files changed, 635 insertions(+), 88 deletions(-) [+]
line wrap: on
line diff
--- a/meth-info-8008688.patch	Tue Apr 23 23:37:55 2013 -0700
+++ b/meth-info-8008688.patch	Thu May 02 19:25:01 2013 -0700
@@ -5,7 +5,7 @@
 diff --git a/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java b/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java
 --- a/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java
 +++ b/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java
-@@ -102,12 +102,12 @@
+@@ -102,16 +102,13 @@
          this.samBase = invokedType.returnType();
  
          this.samMethod = samMethod;
@@ -16,10 +16,15 @@
  
          this.implMethod = implMethod;
 -        this.implInfo = new MethodHandleInfo(implMethod);
+-        // @@@ Temporary work-around pending resolution of 8005119
+-        this.implKind = (implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial)
+-                        ? MethodHandleInfo.REF_invokeVirtual
+-                        : implInfo.getReferenceKind();
 +        this.implInfo = caller.revealDirect(implMethod);
-         // @@@ Temporary work-around pending resolution of 8005119
-         this.implKind = (implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial)
-                         ? MethodHandleInfo.REF_invokeVirtual
++        this.implKind = implInfo.getReferenceKind();
+         this.implIsInstanceMethod =
+                 implKind == MethodHandleInfo.REF_invokeVirtual ||
+                 implKind == MethodHandleInfo.REF_invokeSpecial ||
 diff --git a/src/share/classes/java/lang/invoke/LambdaMetafactory.java b/src/share/classes/java/lang/invoke/LambdaMetafactory.java
 --- a/src/share/classes/java/lang/invoke/LambdaMetafactory.java
 +++ b/src/share/classes/java/lang/invoke/LambdaMetafactory.java
@@ -57,11 +62,49 @@
              assert(MethodHandleNatives.refKindIsMethod(refKind));
              if (clazz.isInterface())
                  assert(refKind == REF_invokeInterface ||
-+                       refKind == REF_invokeStatic ||  //JDK 8
-+                       refKind == REF_invokeSpecial || //JDK 8
++                       refKind == REF_invokeStatic    ||
++                       refKind == REF_invokeSpecial   ||
                         refKind == REF_invokeVirtual && isObjectPublicMethod());
          } else {
              assert(false);
+@@ -268,7 +270,7 @@
+             assert(refKind == REF_invokeSpecial) : this;
+             return true;
+         }
+-        assert(false) : this;
++        assert(false) : this+" != "+MethodHandleNatives.refKindName((byte)originalRefKind);
+         return true;
+     }
+     private boolean staticIsConsistent() {
+@@ -288,6 +290,8 @@
+         } else {
+             if (MethodHandleNatives.refKindDoesDispatch(refKind))
+                 assert(vmindex >= 0) : vmindex + ":" + this;
++            else if (vmindex >= 0 && clazz.isInterface()) //FIXME: delete
++                assert(false) : this; //FIXME: JVM should not install vmindex in this case!
+             else
+                 assert(vmindex < 0) : vmindex;
+             assert(vmtarget instanceof MemberName) : vmtarget + " in " + this;
+@@ -485,14 +489,19 @@
+         if (this.type == null)
+             this.type = new Object[] { m.getReturnType(), m.getParameterTypes() };
+         if (wantSpecial) {
++            assert(!isAbstract()) : this;
+             if (getReferenceKind() == REF_invokeVirtual)
+                 changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
++            else if (getReferenceKind() == REF_invokeInterface)
++                // invokeSpecial on a default method
++                changeReferenceKind(REF_invokeSpecial, REF_invokeInterface);
+         }
+     }
+     public MemberName asSpecial() {
+         switch (getReferenceKind()) {
+         case REF_invokeSpecial:     return this;
+         case REF_invokeVirtual:     return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
++        case REF_invokeInterface:   return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeInterface);
+         case REF_newInvokeSpecial:  return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial);
+         }
+         throw new IllegalArgumentException(this.toString());
 diff --git a/src/share/classes/java/lang/invoke/MethodHandle.java b/src/share/classes/java/lang/invoke/MethodHandle.java
 --- a/src/share/classes/java/lang/invoke/MethodHandle.java
 +++ b/src/share/classes/java/lang/invoke/MethodHandle.java
@@ -232,7 +275,7 @@
 diff --git a/src/share/classes/java/lang/invoke/MethodHandleInfo.java b/src/share/classes/java/lang/invoke/MethodHandleInfo.java
 --- a/src/share/classes/java/lang/invoke/MethodHandleInfo.java
 +++ b/src/share/classes/java/lang/invoke/MethodHandleInfo.java
-@@ -24,80 +24,258 @@
+@@ -24,80 +24,287 @@
   */
  
  package java.lang.invoke;
@@ -333,12 +376,6 @@
 -       REF_invokeSpecial           = Constants.REF_invokeSpecial,
 -       REF_newInvokeSpecial        = Constants.REF_newInvokeSpecial,
 -       REF_invokeInterface         = Constants.REF_invokeInterface;
-+// FIXME:
-+// Should this extend java.lang.reflect.Member?
-+// Should the direct parts be separated into a sub-interface (to allow for future extensions)?
-+//    If we don't have a subinterface, later on we might need an isDirect method.
-+//    If we do have a subinterface, then MethodHandleInfo becomes an empty type (like java.lang.reflect.Type).
-+// Define SecurityManager checks.
 +public
 +interface MethodHandleInfo {
 +    /**
@@ -419,31 +456,56 @@
 -   public MethodType getMethodType() {
 -       return methodType;
 -   }
-+    // /**
-+    //  * Returns the access modifiers of the underlying member method, constructor, or field.
-+    //  * @return the Java language modifiers for the underlying member
-+    //  * @see Modifier
-+    //  */
-+    // public int getModifiers();
-+    // NOTE: modifiers are not part of a symbolic reference, just class/name/type and reference kind
++    // 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
 -   }
-+    /*non-public*/
-+    static class FromMemberName implements MethodHandleInfo {
-+        private final MemberName member;
-+        private final int referenceKind;
++    /**
++     * Reflects the underlying reflected member.
++     * If the underlying member is public, the member is reflected as if by
++     * {@code getMethod}, {@code getConstructor}, or {@code getField}.
++     * Otherwise, it is reflected as if by
++     * {@code getDeclaredMethod}, {@code getDeclaredConstructor}, or {@code getDeclaredField}.
++     * @param <T> the desired type of the result, either {@link Member} or a subtype
++     * @param expected a class object representing the desired result type {@code T}
++     * @return the method, constructor, or field
++     * @throws ReflectiveOperationException if the original lookup object is not able to reflect
++     * @throws ClassCastException if the member is not of the expected type
++     * @throws SecurityException if a security manager is installed and forbids access
++     */
++    public default <T extends Member> T asMember(Class<T> expected) throws ReflectiveOperationException {
++        byte refKind = (byte) getReferenceKind();
++        Class<?> defc = getDeclaringClass();
++        boolean isPublic = Modifier.isPublic(getModifiers());
++        Member mem;
++        if (MethodHandleNatives.refKindIsMethod(refKind)) {
++            if (isPublic)
++                mem = defc.getMethod(getName(), getMethodType().parameterArray());
++            else
++                mem = defc.getDeclaredMethod(getName(), getMethodType().parameterArray());
++        } else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
++            if (isPublic)
++                mem = defc.getConstructor(getMethodType().parameterArray());
++            else
++                mem = defc.getDeclaredConstructor(getMethodType().parameterArray());
++        } else if (MethodHandleNatives.refKindIsField(refKind)) {
++            if (isPublic)
++                mem = defc.getField(getName());
++            else
++                mem = defc.getDeclaredField(getName());
++        } else {
++            throw new IllegalArgumentException("referenceKind="+refKind);
++        }
++        return expected.cast(mem);
++    }
  
 -   public int getReferenceKind() {
 -       return referenceKind;
 -   }
-+        FromMemberName(Lookup lookup, MemberName member, byte referenceKind) {
-+            assert(member.referenceKindIsConsistentWith(referenceKind));
-+            this.member = member;
-+            this.referenceKind = referenceKind;
-+        }
- 
+-
 -   static String getReferenceKindString(int referenceKind) {
 -        switch (referenceKind) {
 -            case REF_getField: return "getfield";
@@ -456,10 +518,50 @@
 -            case REF_newInvokeSpecial: return "newinvokespecial";
 -            case REF_invokeInterface: return "invokeinterface";
 -            default: return "UNKNOWN_REFENCE_KIND" + "[" + referenceKind + "]";
++    /**
++     * Returns the access modifiers of the underlying member method, constructor, or field.
++     * @return the Java language modifiers for the underlying member, or -1 if the value cannot be obtained
++     * @see Modifier
++     */
++    public default int getModifiers() {
++        try {
++            return asMember(Member.class).getModifiers();
++        } catch (ReflectiveOperationException ex) {
++            throw new RuntimeException(ex);
+         }
+     }
+ 
+-    @Override
+-    public String toString() {
+-        return String.format("%s %s.%s:%s", getReferenceKindString(referenceKind),
+-                             declaringClass.getName(), name, methodType);
++    /**
++     * Determines if the underlying member was a variable arity method or constructor.
++     *
++     * @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()  {
++        // not in the public API: Modifier.VARARGS
++        final int ACC_VARARGS = 0x00000080;  // from JVMS 4.6 (Table 4.20)
++        return (getModifiers() & ACC_VARARGS) != 0;
++    }
++
++    /*non-public*/
++    static class FromMemberName implements MethodHandleInfo {
++        private final MemberName member;
++        private final int referenceKind;
++
++        FromMemberName(Lookup lookup, MemberName member, byte referenceKind) {
++            assert(member.referenceKindIsConsistentWith(referenceKind));
++            this.member = member;
++            this.referenceKind = referenceKind;
++        }
++
 +        @Override
 +        public Class<?> getDeclaringClass() {
 +            return member.getDeclaringClass();
-         }
++        }
 +
 +        @Override
 +        public String getName() {
@@ -468,13 +570,18 @@
 +
 +        @Override
 +        public MethodType getMethodType() {
-+            return member.getMethodType();
++            return member.getMethodOrFieldType();
 +        }
 +
-+        // @Override
-+        // public int getModifiers() {
-+        //     return member.getModifiers();
-+        // }
++        @Override
++        public int getModifiers() {
++            return member.getModifiers();
++        }
++
++        @Override
++        public boolean isVarArgs() {
++            return member.isVarargs();  // private spelling has 'args' not 'Args'
++        }
 +
 +        @Override
 +        public int getReferenceKind() {
@@ -482,14 +589,10 @@
 +        }
 +
 +        @Override public String toString() { return MethodHandleInfo.toString(this); }
-+        @Override public boolean equals(Object x) { return MethodHandleInfo.equals(this, x); }
-+        @Override public int hashCode() { return MethodHandleInfo.hashCode(this); }
-     }
- 
--    @Override
--    public String toString() {
--        return String.format("%s %s.%s:%s", getReferenceKindString(referenceKind),
--                             declaringClass.getName(), name, methodType);
++        //@Override public boolean equals(Object x) { return MethodHandleInfo.equals(this, x); }
++        //@Override public int hashCode() { return MethodHandleInfo.hashCode(this); }
++    }
++
 +    /**
 +     * Returns the descriptive name of the given reference kind,
 +     * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
@@ -518,42 +621,25 @@
 +    public static String toString(MethodHandleInfo self) {
 +        return String.format("%s %s.%s:%s", getReferenceKindString(self.getReferenceKind()),
 +                             self.getDeclaringClass().getName(), self.getName(), self.getMethodType());
-+    }
-+
-+    /**
-+     * Compares this symbolic reference to the specified object
-+     * @param self this symbolic reference
-+     * @param x the object to compare this symbolic reference against
-+     * @return {@code true} if and only if the given object represents an equivalent symbolic reference
-+     */
-+    public static boolean equals(MethodHandleInfo self, Object x) {
-+        if (!(x instanceof MethodHandleInfo))
-+            return false;
-+        MethodHandleInfo that = (MethodHandleInfo) x;
-+        return (self.getDeclaringClass() == that.getDeclaringClass() &&
-+                self.getName().equals(that.getName()) &&
-+                self.getMethodType().equals(that.getMethodType()) &&
-+                self.getReferenceKind() == that.getReferenceKind());
-+    }
-+
-+    /**
-+     * Returns a hash code for this symbolic reference.
-+     * It is computed as if by {@link java.util.Objects#hash} on the
-+     * declaring class, the name, the method type, and the reference kind, in that order.
-+     * @param self this symbolic reference
-+     * @return a hash code for this symbolic reference, computed as if by {@link java.util.Objects#hash}
-+     */
-+    public static int hashCode(MethodHandleInfo self) {
-+        return Objects.hash(self.getDeclaringClass(),
-+                            self.getName(),
-+                            self.getMethodType(),
-+                            self.getReferenceKind());
      }
  }
+diff --git a/src/share/classes/java/lang/invoke/MethodHandleNatives.java b/src/share/classes/java/lang/invoke/MethodHandleNatives.java
+--- a/src/share/classes/java/lang/invoke/MethodHandleNatives.java
++++ b/src/share/classes/java/lang/invoke/MethodHandleNatives.java
+@@ -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;
 diff --git a/src/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/share/classes/java/lang/invoke/MethodHandleProxies.java
 --- a/src/share/classes/java/lang/invoke/MethodHandleProxies.java
 +++ b/src/share/classes/java/lang/invoke/MethodHandleProxies.java
-@@ -107,8 +107,9 @@
+@@ -108,8 +108,9 @@
       * Future versions of this API may also equip wrapper instances
       * with one or more additional public "marker" interfaces.
       *
@@ -567,15 +653,25 @@
 diff --git a/src/share/classes/java/lang/invoke/MethodHandles.java b/src/share/classes/java/lang/invoke/MethodHandles.java
 --- a/src/share/classes/java/lang/invoke/MethodHandles.java
 +++ b/src/share/classes/java/lang/invoke/MethodHandles.java
-@@ -64,6 +64,7 @@
+@@ -26,8 +26,7 @@
+ package java.lang.invoke;
+ 
+ import java.lang.reflect.*;
+-import java.security.AccessController;
+-import java.security.PrivilegedAction;
++import java.security.*;
+ import java.util.List;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+@@ -68,6 +67,7 @@
       * including direct method handles to private fields and methods.
       * This lookup object is a <em>capability</em> which may be delegated to trusted agents.
       * Do not store it in place where untrusted code can access it.
 +     * @return a lookup object for the caller of this method
       */
+     @CallerSensitive
      public static Lookup lookup() {
-         return new Lookup();
-@@ -81,6 +82,7 @@
+@@ -86,6 +86,7 @@
       * {@linkplain Lookup#in <code>publicLookup().in(C.class)</code>}.
       * Since all classes have equal access to public names,
       * such a change would confer no new access rights.
@@ -583,7 +679,7 @@
       */
      public static Lookup publicLookup() {
          return Lookup.PUBLIC_LOOKUP;
-@@ -104,72 +106,73 @@
+@@ -109,72 +110,73 @@
       * on the {@code Lookup} object to create method handles for access-checked members.
       * This includes all methods, constructors, and fields which are allowed to the lookup class,
       * even private ones.
@@ -688,7 +784,7 @@
       * Both {@code MT} and the field type {@code FT} are documented as a parameter named {@code type}.
       * The formal parameter {@code this} stands for the self-reference of type {@code C};
       * if it is present, it is always the leading argument to the method handle invocation.
-@@ -203,7 +206,7 @@
+@@ -208,7 +210,7 @@
       * security manager checks.
       * </ul>
       *
@@ -697,7 +793,7 @@
       * Access checks are applied in the factory methods of {@code Lookup},
       * when a method handle is created.
       * This is a key difference from the Core Reflection API, since
-@@ -290,7 +293,7 @@
+@@ -295,7 +297,7 @@
       * with static methods of {@link MethodHandles},
       * independently of any {@code Lookup} object.
       *
@@ -706,7 +802,7 @@
       * <a name="secmgr"></a>
       * If a security manager is present, member lookups are subject to
       * additional checks.
-@@ -381,6 +384,7 @@
+@@ -386,6 +388,7 @@
           *  but the permissions may be additionally limited by the bitmask
           *  {@link #lookupModes lookupModes}, which controls whether non-public members
           *  can be accessed.
@@ -714,7 +810,7 @@
           */
          public Class<?> lookupClass() {
              return lookupClass;
-@@ -407,6 +411,7 @@
+@@ -412,6 +415,7 @@
           *  The purpose of this is to restrict access via the new lookup object,
           *  so that it can access only names which can be reached by the original
           *  lookup object, and also by the new lookup class.
@@ -722,12 +818,16 @@
           */
          public int lookupModes() {
              return allowedModes & ALL_MODES;
-@@ -1023,6 +1028,27 @@
+@@ -1022,6 +1026,86 @@
              return unreflectField(f, true);
          }
  
 +        /**
 +         * Crack 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
 +         * @throws ReflectiveOperationException if the cracking fails due to access limitations,
@@ -743,14 +843,93 @@
 +                // To avoid creating extra MemberName objects for this common case,
 +                // we encode this extra degree of freedom using MH.isInvokeSpecial.
 +                refKind = REF_invokeVirtual;
-+            //FIXME: Need to check permissions.
++            // Check SM permissions and member access:
++            Class<?> defc = member.getDeclaringClass();
++            if (defc != lookupClass) {  // quick cutouts
++                if (allowedModes != TRUSTED) {
++                    checkSecurityManager(defc, member, lookupClass());
++                    checkAccess(member.getReferenceKind(), defc, member);
++                }
++            }
 +            return new MethodHandleInfo.FromMemberName(this, member, refKind);
 +        }
 +
++        /**
++         * Crack a direct method handle created by this lookup object or a similar one.
++         * If {@code bypassAccessibilityChecks} is true, performs the accessibility check
++         * as calling {@link AccessibleObject#setAccessible AccessibleObject.setAccessible}.
++         * If {@code bypassAccessibilityChecks} is false, the same checks are performed
++         * as for the {@linkplain #revealDirect(MethodHandle) one-argument version} of this method.
++         * @param target a direct method handle to crack into symbolic reference components
++         * @param bypassAccessibilityChecks 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
++         * @throws ReflectiveOperationException if the cracking fails due to access limitations,
++         *         or because the target is not a direct method handle
++         */
++        public MethodHandleInfo revealDirect(MethodHandle target, boolean bypassAccessibilityChecks) throws ReflectiveOperationException {
++            if (!bypassAccessibilityChecks || allowedModes == TRUSTED)
++                return revealDirect(target);
++            SecurityManager smgr = System.getSecurityManager();
++            if (smgr != null) {
++                try {
++                    // The following is done just for side effect:
++                    Lookup.class.getDeclaredField("IMPL_LOOKUP").setAccessible(true);
++                } catch (SecurityException ex) {
++                    throw ex;
++                } catch (ReflectiveOperationException ex) {
++                    throw newInternalError(ex);
++                }
++                // If we get here, we know we are privileged enough to do any reveal.
++            }
++            return IMPL_LOOKUP.revealDirect(target);
++        }
++        /** A copy of {@code java.lang.reflect.AccessibleObject.ACCESS_PERMISSION}. */
++        private static final Permission ACCESS_PERMISSION = makeAccessPermission(false);
++        static private Permission makeAccessPermission(boolean slowly) {
++            final Permission perm = new ReflectPermission("suppressAccessChecks");
++            if (!slowly)  return perm;
++            assert(perm.equals(makeAccessPermission(true)));
++            return AccessController.doPrivileged(new PrivilegedAction<Permission>() {
++                    public Permission run() {
++                        try {
++                            Field f = AccessibleObject.class.getDeclaredField("ACCESS_PERMISSION");
++                            f.setAccessible(true);
++                            return (Permission) f.get(null);
++                        } catch (ReflectiveOperationException ex) {
++                            throw newInternalError(ex);
++                        }
++                    }
++                });
++        }
++
          /// Helper methods, all package-private.
  
          MemberName resolveOrFail(byte refKind, Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
-@@ -1324,6 +1350,7 @@
+@@ -1264,6 +1348,7 @@
+             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
+@@ -1292,6 +1377,7 @@
+             return mh;
+         }
+         private MethodHandle fakeMethodHandleInvoke(MemberName method) {
++            // FIXME: MethodHandleInfo should be able to crack this.
+             return throwException(method.getReturnType(), UnsupportedOperationException.class);
+         }
+         private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
+@@ -1304,6 +1390,7 @@
+                 hostClass = callerClass;  // callerClass came from a security manager style stack walk
+             MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass);
+             // Note: caller will apply varargs after this step happens.
++            // FIXME: MethodHandleInfo should be able to crack this.
+             return cbmh;
+         }
+         private MethodHandle getDirectField(byte refKind, Class<?> refc, MemberName field) throws IllegalAccessException {
+@@ -1371,6 +1458,7 @@
       * The type of the method handle will have a void return type.
       * Its last argument will be the array's element type.
       * The first and second arguments will be the array type and int.
@@ -758,7 +937,23 @@
       * @return a method handle which can store values into the array type
       * @throws NullPointerException if the argument is null
       * @throws IllegalArgumentException if arrayClass is not an array type
-@@ -1552,12 +1579,12 @@
+@@ -1468,6 +1556,7 @@
+      */
+     static public
+     MethodHandle exactInvoker(MethodType type) {
++        // FIXME: MethodHandleInfo should be able to crack this.
+         return type.invokers().exactInvoker();
+     }
+ 
+@@ -1501,6 +1590,7 @@
+      */
+     static public
+     MethodHandle invoker(MethodType type) {
++        // FIXME: MethodHandleInfo should be able to crack this.
+         return type.invokers().generalInvoker();
+     }
+ 
+@@ -1599,12 +1689,12 @@
  ...
  MethodType intfn1 = methodType(int.class, int.class);
  MethodType intfn2 = methodType(int.class, int.class, int.class);
@@ -773,7 +968,7 @@
  assert(add.type().equals(intfn2));
  MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
  assert(twice.type().equals(intfn1));
-@@ -2233,6 +2260,8 @@
+@@ -2280,6 +2370,8 @@
       * The method type will nominally specify a return of {@code returnType}.
       * The return type may be anything convenient:  It doesn't matter to the
       * method handle's behavior, since it will never return normally.
@@ -901,3 +1096,355 @@
   * As long as each bootstrap method can be correctly invoked
   * by {@code MethodHandle.invoke}, its detailed type is arbitrary.
   * For example, the first argument could be {@code Object}
+diff --git a/test/java/lang/invoke/RevealDirectTest.java b/test/java/lang/invoke/RevealDirectTest.java
+new file mode 100644
+--- /dev/null
++++ b/test/java/lang/invoke/RevealDirectTest.java
+@@ -0,0 +1,347 @@
++/*
++ * 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
++ * @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 junit/othervm -ea -esa -DSETSMGR test.java.lang.invoke.RevealDirectTest
++ */
++
++/* To run manually:
++ * $ $JAVA8X_HOME/bin/javac -cp ~/env/jars/junit-4.9.jar -d ../../../.. RevealDirectTest.java
++ * $ $JAVA8X_HOME/bin/java  -cp ~/env/jars/junit-4.9.jar:../../../.. -ea -esa org.junit.runner.JUnitCore 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 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(); } //FIXME: include
++        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 = getMembers(lookup().lookupClass());
++    }
++
++    static boolean VERBOSE = false;
++
++    @Test public void testSimple() throws Throwable {
++        testOnClass(Simple.localLookup(), Simple.class, Simple.MEMBERS);
++    }
++    @Test public void testPublicLookup() throws Throwable {
++        testOnClass(publicLookup(), Simple.class, publicOnly(Simple.MEMBERS));
++    }
++    @Test public void testWithSecurityManager() throws Throwable {
++        if (System.getSecurityManager() != null) {
++            System.out.println("security manager already installed; existing unit test");
++            return;
++        }
++        System.setSecurityManager(new SecurityManager());
++        testOnClass(Simple.localLookup(), Simple.class, publicOnly(Simple.MEMBERS));
++    }
++
++    static List<Member> getMembers(Class<?> cls) {
++        ArrayList<Member> res = new ArrayList<>();
++        for (Class<?> sup : getSupers(cls)) {
++            res.addAll(Arrays.asList(sup.getDeclaredFields()));
++            res.addAll(Arrays.asList(sup.getDeclaredMethods()));
++            res.addAll(Arrays.asList(sup.getDeclaredConstructors()));
++        }
++        for (int i = 0; i < res.size(); i++) {
++            Member mem = res.get(i);
++            if (!canBeReached(mem, cls) ||
++                res.indexOf(mem) != i ||
++                mem.isSynthetic()) {
++                res.remove(i--);
++            }
++        }
++        //System.out.println("getMembers => "+res);
++        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 List<Member> publicOnly(List<Member> members) {
++        members = new ArrayList<>(members);
++        for (Iterator<Member> i = members.iterator(); i.hasNext(); ) {
++            Member mem = i.next();
++            int mods = mem.getModifiers();
++            mods &= mem.getDeclaringClass().getModifiers();
++            if (!Modifier.isPublic(mods))
++                i.remove();
++        }
++        return members;
++    }
++
++    void testOnClass(Lookup lookup, Class<?> cls) throws Throwable {
++        testOnClass(lookup, cls, getMembers(cls));
++    }
++    void testOnClass(Lookup lookup, Class<?> cls, List<Member> mems) throws Throwable {
++        for (Member mem : mems) {
++            testWithMember(lookup, mem);
++        }
++    }
++    static class UnreflectResult {  // a tuple
++        final MethodHandle mh;
++        final byte kind;
++        final Member mem;
++        final int var;
++        UnreflectResult(MethodHandle mh, byte kind, Member mem, int var) {
++            this.mh = mh;
++            this.kind = kind;
++            this.mem = mem;
++            this.var = var;
++        }
++    }
++    static UnreflectResult unreflectMember(Lookup lookup, Member mem, int variation) 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;
++            switch (variation) {
++            case 0:
++                if (isStatic)
++                    mh = lookup.findStatic(defc, name, type);
++                else
++                    mh = lookup.findVirtual(defc, name, type);
++                break;
++            case 1:
++                mh = lookup.unreflect(m);
++                break;
++            case 2:
++                if (!canBeSpecial)
++                    break;
++                mh = lookup.findSpecial(defc, name, type, lookup.lookupClass());
++                break;
++            case 3:
++                if (!canBeSpecial)
++                    break;
++                mh = lookup.unreflectSpecial(m, lookup.lookupClass());
++                break;
++            }
++        } else if (mem instanceof Constructor) {
++            name = "<init>";  // not used
++            Constructor<?> m = (Constructor<?>) mem;
++            MethodType type = methodType(void.class, m.getParameterTypes());
++            kind = REF_newInvokeSpecial;
++            switch (variation) {
++            case 0:
++                mh = lookup.findConstructor(defc, type);
++                break;
++            case 1:
++                mh = lookup.unreflectConstructor(m);
++                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);
++            switch (variation) {
++            case 0:
++                if (isStatic)
++                    mh = lookup.findStaticGetter(defc, name, type);
++                else
++                    mh = lookup.findGetter(defc, name, type);
++                break;
++            case 1:
++                mh = lookup.unreflectGetter(m);
++                break;
++            case 2:
++                if (!canHaveSetter)
++                    break;
++                if (isStatic)
++                    mh = lookup.findStaticSetter(defc, name, type);
++                else
++                    mh = lookup.findSetter(defc, name, type);
++                break;
++            case 3:
++                if (!canHaveSetter)
++                    break;
++                mh = lookup.unreflectSetter(m);
++                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.kind == info.getReferenceKind());
++        assert(res.mem.getModifiers() == info.getModifiers());
++        assert(res.mem.getDeclaringClass() == info.getDeclaringClass());
++        String expectName = res.mem.getName();
++        if (res.kind == REF_newInvokeSpecial)
++            expectName = "<init>";
++        assert(expectName.equals(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);
++        assert(expectType.equals(info.getMethodType()));
++        assert(res.mh.isVarargsCollector() == info.isVarArgs());
++        return true;
++    }
++    static boolean consistent(Member mem, Member mem2) {
++        assert(mem.equals(mem2));
++        return true;
++    }
++    static boolean consistent(MethodHandleInfo info, MethodHandleInfo info2) {
++        assert(info.getReferenceKind() == info2.getReferenceKind());
++        assert(info.getModifiers() == info2.getModifiers());
++        assert(info.getDeclaringClass() == info2.getDeclaringClass());
++        assert(info.getName().equals(info2.getName()));
++        assert(info.getMethodType().equals(info2.getMethodType()));
++        assert(info.isVarArgs() == info2.isVarArgs());
++        return true;
++    }
++    static boolean consistent(MethodHandle mh, MethodHandle mh2) {
++        assert(mh.type().equals(mh2.type()));
++        assert(mh.isVarargsCollector() == mh2.isVarargsCollector());
++        return true;
++    }
++    void testWithMember(Lookup lookup, Member mem) throws Throwable {
++        for (int variation = 0; ; variation++) {
++            UnreflectResult res = unreflectMember(lookup, mem, variation);
++            if (res == null)  break;
++            if (VERBOSE && variation == 0)
++                System.out.println("from "+mem.getDeclaringClass().getSimpleName());
++            MethodHandle mh = res.mh;
++            MethodHandleInfo info = lookup.revealDirect(mh);
++            if (VERBOSE)  System.out.println("  "+variation+": "+info+"  << "+mh);
++            assert(consistent(res, info));
++            Member mem2 = info.asMember(Member.class);
++            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 = info2.asMember(Member.class);
++            assert(consistent(mem2, mem3));
++        }
++    }
++}