jdk/src/share/classes/java/lang/invoke/MemberName.java
changeset 13423 17843fff200d
parent 13421 d20670384420
child 13424 5de7e57cdc02
--- a/jdk/src/share/classes/java/lang/invoke/MemberName.java	Thu Jul 12 00:12:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MemberName.java	Tue Jul 24 10:47:44 2012 -0700
@@ -26,6 +26,8 @@
 package java.lang.invoke;
 
 import sun.invoke.util.BytecodeDescriptor;
+import sun.invoke.util.VerifyAccess;
+
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
@@ -38,6 +40,7 @@
 import java.util.List;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import static java.lang.invoke.MethodHandleStatics.*;
+import java.util.Objects;
 
 /**
  * A {@code MemberName} is a compact symbolic datum which fully characterizes
@@ -71,19 +74,14 @@
     private String     name;        // may be null if not yet materialized
     private Object     type;        // may be null if not yet materialized
     private int        flags;       // modifier bits; see reflect.Modifier
-
-    private Object     vmtarget;    // VM-specific target value
-    private int        vmindex;     // method index within class or interface
-
-    { vmindex = VM_INDEX_UNINITIALIZED; }
+    //@Injected JVM_Method* vmtarget;
+    //@Injected int         vmindex;
+    private Object     resolution;  // if null, this guy is resolved
 
     /** Return the declaring class of this member.
      *  In the case of a bare name and type, the declaring class will be null.
      */
     public Class<?> getDeclaringClass() {
-        if (clazz == null && isResolved()) {
-            expandFromVM();
-        }
         return clazz;
     }
 
@@ -105,6 +103,16 @@
         return name;
     }
 
+    public MethodType getMethodOrFieldType() {
+        if (isInvocable())
+            return getMethodType();
+        if (isGetter())
+            return MethodType.methodType(getFieldType());
+        if (isSetter())
+            return MethodType.methodType(void.class, getFieldType());
+        throw new InternalError("not a method or field: "+this);
+    }
+
     /** Return the declared type of this member, which
      *  must be a method or constructor.
      */
@@ -140,9 +148,11 @@
      *  a reference to declaring class.  For static methods, it is the same as the declared type.
      */
     public MethodType getInvocationType() {
-        MethodType itype = getMethodType();
+        MethodType itype = getMethodOrFieldType();
+        if (isConstructor() && getReferenceKind() == REF_newInvokeSpecial)
+            return itype.changeReturnType(clazz);
         if (!isStatic())
-            itype = itype.insertParameterTypes(0, clazz);
+            return itype.insertParameterTypes(0, clazz);
         return itype;
     }
 
@@ -208,9 +218,98 @@
         return (flags & RECOGNIZED_MODIFIERS);
     }
 
+    /** Return the reference kind of this member, or zero if none.
+     */
+    public byte getReferenceKind() {
+        return (byte) ((flags >>> MN_REFERENCE_KIND_SHIFT) & MN_REFERENCE_KIND_MASK);
+    }
+    private boolean referenceKindIsConsistent() {
+        byte refKind = getReferenceKind();
+        if (refKind == REF_NONE)  return isType();
+        if (isField()) {
+            assert(staticIsConsistent());
+            assert(MethodHandleNatives.refKindIsField(refKind));
+        } else if (isConstructor()) {
+            assert(refKind == REF_newInvokeSpecial || refKind == REF_invokeSpecial);
+        } else if (isMethod()) {
+            assert(staticIsConsistent());
+            assert(MethodHandleNatives.refKindIsMethod(refKind));
+            if (clazz.isInterface())
+                assert(refKind == REF_invokeInterface ||
+                       refKind == REF_invokeVirtual && isObjectPublicMethod());
+        } else {
+            assert(false);
+        }
+        return true;
+    }
+    private boolean isObjectPublicMethod() {
+        if (clazz == Object.class)  return true;
+        MethodType mtype = getMethodType();
+        if (name.equals("toString") && mtype.returnType() == String.class && mtype.parameterCount() == 0)
+            return true;
+        if (name.equals("hashCode") && mtype.returnType() == int.class && mtype.parameterCount() == 0)
+            return true;
+        if (name.equals("equals") && mtype.returnType() == boolean.class && mtype.parameterCount() == 1 && mtype.parameterType(0) == Object.class)
+            return true;
+        return false;
+    }
+    /*non-public*/ boolean referenceKindIsConsistentWith(int originalRefKind) {
+        int refKind = getReferenceKind();
+        if (refKind == originalRefKind)  return true;
+        switch (originalRefKind) {
+        case REF_invokeInterface:
+            // Looking up an interface method, can get (e.g.) Object.hashCode
+            assert(refKind == REF_invokeVirtual ||
+                   refKind == REF_invokeSpecial) : this;
+            return true;
+        case REF_invokeVirtual:
+        case REF_newInvokeSpecial:
+            // Looked up a virtual, can get (e.g.) final String.hashCode.
+            assert(refKind == REF_invokeSpecial) : this;
+            return true;
+        }
+        assert(false) : this;
+        return true;
+    }
+    private boolean staticIsConsistent() {
+        byte refKind = getReferenceKind();
+        return MethodHandleNatives.refKindIsStatic(refKind) == isStatic() || getModifiers() == 0;
+    }
+    private boolean vminfoIsConsistent() {
+        byte refKind = getReferenceKind();
+        assert(isResolved());  // else don't call
+        Object vminfo = MethodHandleNatives.getMemberVMInfo(this);
+        assert(vminfo instanceof Object[]);
+        long vmindex = (Long) ((Object[])vminfo)[0];
+        Object vmtarget = ((Object[])vminfo)[1];
+        if (MethodHandleNatives.refKindIsField(refKind)) {
+            assert(vmindex >= 0) : vmindex + ":" + this;
+            assert(vmtarget instanceof Class);
+        } else {
+            if (MethodHandleNatives.refKindDoesDispatch(refKind))
+                assert(vmindex >= 0) : vmindex + ":" + this;
+            else
+                assert(vmindex < 0) : vmindex;
+            assert(vmtarget instanceof MemberName) : vmtarget + " in " + this;
+        }
+        return true;
+    }
+
+    private MemberName changeReferenceKind(byte refKind, byte oldKind) {
+        assert(getReferenceKind() == oldKind);
+        assert(MethodHandleNatives.refKindIsValid(refKind));
+        flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT);
+//        if (isConstructor() && refKind != REF_newInvokeSpecial)
+//            flags += (IS_METHOD - IS_CONSTRUCTOR);
+//        else if (refKind == REF_newInvokeSpecial && isMethod())
+//            flags += (IS_CONSTRUCTOR - IS_METHOD);
+        return this;
+    }
+
     private void setFlags(int flags) {
         this.flags = flags;
         assert(testAnyFlags(ALL_KINDS));
+        assert(referenceKindIsConsistent());
     }
 
     private boolean testFlags(int mask, int value) {
@@ -223,6 +322,17 @@
         return !testFlags(mask, 0);
     }
 
+    /** 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 negs = Modifier.STATIC;
+        if (testFlags(bits | negs, bits) &&
+            clazz == MethodHandle.class) {
+            return name.equals("invoke") || name.equals("invokeExact");
+        }
+        return false;
+    }
+
     /** Utility method to query the modifier flags of this member. */
     public boolean isStatic() {
         return Modifier.isStatic(flags);
@@ -243,10 +353,22 @@
     public boolean isFinal() {
         return Modifier.isFinal(flags);
     }
+    /** Utility method to query whether this member or its defining class is final. */
+    public boolean canBeStaticallyBound() {
+        return Modifier.isFinal(flags | clazz.getModifiers());
+    }
+    /** Utility method to query the modifier flags of this member. */
+    public boolean isVolatile() {
+        return Modifier.isVolatile(flags);
+    }
     /** Utility method to query the modifier flags of this member. */
     public boolean isAbstract() {
         return Modifier.isAbstract(flags);
     }
+    /** Utility method to query the modifier flags of this member. */
+    public boolean isNative() {
+        return Modifier.isNative(flags);
+    }
     // let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo
 
     // unofficial modifier flags, used by HotSpot:
@@ -279,15 +401,12 @@
             IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor
             IS_FIELD       = MN_IS_FIELD,       // field
             IS_TYPE        = MN_IS_TYPE;        // nested type
-    static final int  // for MethodHandleNatives.getMembers
-            SEARCH_SUPERCLASSES = MN_SEARCH_SUPERCLASSES,
-            SEARCH_INTERFACES   = MN_SEARCH_INTERFACES;
 
     static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED;
     static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE;
     static final int IS_INVOCABLE = IS_METHOD | IS_CONSTRUCTOR;
     static final int IS_FIELD_OR_METHOD = IS_METHOD | IS_FIELD;
-    static final int SEARCH_ALL_SUPERS = SEARCH_SUPERCLASSES | SEARCH_INTERFACES;
+    static final int SEARCH_ALL_SUPERS = MN_SEARCH_SUPERCLASSES | MN_SEARCH_INTERFACES;
 
     /** Utility method to query whether this member is a method or constructor. */
     public boolean isInvocable() {
@@ -318,6 +437,12 @@
         return !testAnyFlags(ALL_ACCESS);
     }
 
+    /** Utility method to query whether this member is accessible from a given lookup class. */
+    public boolean isAccessibleFrom(Class<?> lookupClass) {
+        return VerifyAccess.isMemberAccessible(this.getDeclaringClass(), this.getDeclaringClass(), flags,
+                                               lookupClass, ALL_ACCESS|MethodHandles.Lookup.PACKAGE);
+    }
+
     /** Initialize a query.   It is not resolved. */
     private void init(Class<?> defClass, String name, Object type, int flags) {
         // defining class is allowed to be null (for a naked name/type pair)
@@ -328,7 +453,7 @@
         this.name = name;
         this.type = type;
         setFlags(flags);
-        assert(!isResolved());
+        assert(this.resolution == null);  // nobody should have touched this yet
     }
 
     private void expandFromVM() {
@@ -339,39 +464,91 @@
     }
 
     // Capturing information from the Core Reflection API:
-    private static int flagsMods(int flags, int mods) {
+    private static int flagsMods(int flags, int mods, byte refKind) {
         assert((flags & RECOGNIZED_MODIFIERS) == 0);
         assert((mods & ~RECOGNIZED_MODIFIERS) == 0);
-        return flags | mods;
+        assert((refKind & ~MN_REFERENCE_KIND_MASK) == 0);
+        return flags | mods | (refKind << MN_REFERENCE_KIND_SHIFT);
     }
     /** Create a name for the given reflected method.  The resulting name will be in a resolved state. */
     public MemberName(Method m) {
-        Object[] typeInfo = { m.getReturnType(), m.getParameterTypes() };
-        init(m.getDeclaringClass(), m.getName(), typeInfo, flagsMods(IS_METHOD, m.getModifiers()));
+        this(m, false);
+    }
+    @SuppressWarnings("LeakingThisInConstructor")
+    public MemberName(Method m, boolean wantSpecial) {
         // fill in vmtarget, vmindex while we have m in hand:
         MethodHandleNatives.init(this, m);
-        assert(isResolved());
+        assert(isResolved() && this.clazz != null);
+        this.name = m.getName();
+        if (this.type == null)
+            this.type = new Object[] { m.getReturnType(), m.getParameterTypes() };
+        if (wantSpecial) {
+            if (getReferenceKind() == REF_invokeVirtual)
+                changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
+        }
+    }
+    public MemberName asSpecial() {
+        switch (getReferenceKind()) {
+        case REF_invokeSpecial:     return this;
+        case REF_invokeVirtual:     return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
+        case REF_newInvokeSpecial:  return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial);
+        }
+        throw new IllegalArgumentException(this.toString());
+    }
+    public MemberName asConstructor() {
+        switch (getReferenceKind()) {
+        case REF_invokeSpecial:     return clone().changeReferenceKind(REF_newInvokeSpecial, REF_invokeSpecial);
+        case REF_newInvokeSpecial:  return this;
+        }
+        throw new IllegalArgumentException(this.toString());
     }
     /** Create a name for the given reflected constructor.  The resulting name will be in a resolved state. */
+    @SuppressWarnings("LeakingThisInConstructor")
     public MemberName(Constructor<?> ctor) {
-        Object[] typeInfo = { void.class, ctor.getParameterTypes() };
-        init(ctor.getDeclaringClass(), CONSTRUCTOR_NAME, typeInfo, flagsMods(IS_CONSTRUCTOR, ctor.getModifiers()));
         // fill in vmtarget, vmindex while we have ctor in hand:
         MethodHandleNatives.init(this, ctor);
-        assert(isResolved());
+        assert(isResolved() && this.clazz != null);
+        this.name = CONSTRUCTOR_NAME;
+        if (this.type == null)
+            this.type = new Object[] { void.class, ctor.getParameterTypes() };
     }
-    /** Create a name for the given reflected field.  The resulting name will be in a resolved state. */
+    /** Create a name for the given reflected field.  The resulting name will be in a resolved state.
+     */
     public MemberName(Field fld) {
-        init(fld.getDeclaringClass(), fld.getName(), fld.getType(), flagsMods(IS_FIELD, fld.getModifiers()));
+        this(fld, false);
+    }
+    @SuppressWarnings("LeakingThisInConstructor")
+    public MemberName(Field fld, boolean makeSetter) {
         // fill in vmtarget, vmindex while we have fld in hand:
         MethodHandleNatives.init(this, fld);
-        assert(isResolved());
+        assert(isResolved() && this.clazz != null);
+        this.name = fld.getName();
+        this.type = fld.getType();
+        assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField));
+        byte refKind = this.getReferenceKind();
+        assert(refKind == (isStatic() ? REF_getStatic : REF_getField));
+        if (makeSetter) {
+            changeReferenceKind((byte)(refKind + (REF_putStatic - REF_getStatic)), refKind);
+        }
+    }
+    public boolean isGetter() {
+        return MethodHandleNatives.refKindIsGetter(getReferenceKind());
+    }
+    public boolean isSetter() {
+        return MethodHandleNatives.refKindIsSetter(getReferenceKind());
+    }
+    public MemberName asSetter() {
+        byte refKind = getReferenceKind();
+        assert(MethodHandleNatives.refKindIsGetter(refKind));
+        assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField));
+        byte setterRefKind = (byte)(refKind + (REF_putField - REF_getField));
+        return clone().changeReferenceKind(setterRefKind, refKind);
     }
     /** Create a name for the given class.  The resulting name will be in a resolved state. */
     public MemberName(Class<?> type) {
-        init(type.getDeclaringClass(), type.getSimpleName(), type, flagsMods(IS_TYPE, type.getModifiers()));
-        vmindex = 0;  // isResolved
-        assert(isResolved());
+        init(type.getDeclaringClass(), type.getSimpleName(), type,
+                flagsMods(IS_TYPE, type.getModifiers(), REF_NONE));
+        initResolved(true);
     }
 
     // bare-bones constructor; the JVM will fill it in
@@ -386,41 +563,89 @@
         }
      }
 
-    // %%% define equals/hashcode?
+    /** Get the definition of this member name.
+     *  This may be in a super-class of the declaring class of this member.
+     */
+    public MemberName getDefinition() {
+        if (!isResolved())  throw new IllegalStateException("must be resolved: "+this);
+        if (isType())  return this;
+        MemberName res = this.clone();
+        res.clazz = null;
+        res.type = null;
+        res.name = null;
+        res.resolution = res;
+        res.expandFromVM();
+        assert(res.getName().equals(this.getName()));
+        return res;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(clazz, flags, name, getType());
+    }
+    @Override
+    public boolean equals(Object that) {
+        return (that instanceof MemberName && this.equals((MemberName)that));
+    }
+
+    /** Decide if two member names have exactly the same symbolic content.
+     *  Does not take into account any actual class members, so even if
+     *  two member names resolve to the same actual member, they may
+     *  be distinct references.
+     */
+    public boolean equals(MemberName that) {
+        if (this == that)  return true;
+        if (that == null)  return false;
+        return this.clazz == that.clazz
+                && this.flags == that.flags
+                && Objects.equals(this.name, that.name)
+                && Objects.equals(this.getType(), that.getType());
+    }
 
     // Construction from symbolic parts, for queries:
-    /** Create a field or type name from the given components:  Declaring class, name, type, modifiers.
+    /** Create a field or type name from the given components:  Declaring class, name, type, reference kind.
      *  The declaring class may be supplied as null if this is to be a bare name and type.
      *  The resulting name will in an unresolved state.
      */
-    public MemberName(Class<?> defClass, String name, Class<?> type, int modifiers) {
-        init(defClass, name, type, IS_FIELD | (modifiers & RECOGNIZED_MODIFIERS));
+    public MemberName(Class<?> defClass, String name, Class<?> type, byte refKind) {
+        init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind));
+        initResolved(false);
     }
     /** Create a field or type name from the given components:  Declaring class, name, type.
      *  The declaring class may be supplied as null if this is to be a bare name and type.
      *  The modifier flags default to zero.
      *  The resulting name will in an unresolved state.
      */
-    public MemberName(Class<?> defClass, String name, Class<?> type) {
-        this(defClass, name, type, 0);
+    public MemberName(Class<?> defClass, String name, Class<?> type, Void unused) {
+        this(defClass, name, type, REF_NONE);
+        initResolved(false);
     }
     /** Create a method or constructor name from the given components:  Declaring class, name, type, modifiers.
      *  It will be a constructor if and only if the name is {@code "&lt;init&gt;"}.
      *  The declaring class may be supplied as null if this is to be a bare name and type.
+     *  The last argument is optional, a boolean which requests REF_invokeSpecial.
      *  The resulting name will in an unresolved state.
      */
-    public MemberName(Class<?> defClass, String name, MethodType type, int modifiers) {
-        int flagBit = (name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
-        init(defClass, name, type, flagBit | (modifiers & RECOGNIZED_MODIFIERS));
+    public MemberName(Class<?> defClass, String name, MethodType type, byte refKind) {
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
+        int flags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
+        init(defClass, name, type, flagsMods(flags, 0, refKind));
+        initResolved(false);
     }
-    /** Create a method or constructor name from the given components:  Declaring class, name, type, modifiers.
-     *  It will be a constructor if and only if the name is {@code "&lt;init&gt;"}.
-     *  The declaring class may be supplied as null if this is to be a bare name and type.
-     *  The modifier flags default to zero.
-     *  The resulting name will in an unresolved state.
+//    /** Create a method or constructor name from the given components:  Declaring class, name, type, modifiers.
+//     *  It will be a constructor if and only if the name is {@code "&lt;init&gt;"}.
+//     *  The declaring class may be supplied as null if this is to be a bare name and type.
+//     *  The modifier flags default to zero.
+//     *  The resulting name will in an unresolved state.
+//     */
+//    public MemberName(Class<?> defClass, String name, MethodType type, Void unused) {
+//        this(defClass, name, type, REF_NONE);
+//    }
+
+    /** Query whether this member name is resolved to a non-static, non-final method.
      */
-    public MemberName(Class<?> defClass, String name, MethodType type) {
-        this(defClass, name, type, 0);
+    public boolean hasReceiverTypeDispatch() {
+        return MethodHandleNatives.refKindDoesDispatch(getReferenceKind());
     }
 
     /** Query whether this member name is resolved.
@@ -429,15 +654,38 @@
      *  (Document?)
      */
     public boolean isResolved() {
-        return (vmindex != VM_INDEX_UNINITIALIZED);
+        return resolution == null;
+    }
+
+    private void initResolved(boolean isResolved) {
+        assert(this.resolution == null);  // not initialized yet!
+        if (!isResolved)
+            this.resolution = this;
+        assert(isResolved() == isResolved);
     }
 
-    /** Query whether this member name is resolved to a non-static, non-final method.
-     */
-    public boolean hasReceiverTypeDispatch() {
-        return (isMethod() && getVMIndex() >= 0);
+    void checkForTypeAlias() {
+        if (isInvocable()) {
+            MethodType type;
+            if (this.type instanceof MethodType)
+                type = (MethodType) this.type;
+            else
+                this.type = type = getMethodType();
+            if (type.erase() == type)  return;
+            if (VerifyAccess.isTypeVisible(type, clazz))  return;
+            throw new LinkageError("bad method type alias: "+type+" not visible from "+clazz);
+        } else {
+            Class<?> type;
+            if (this.type instanceof Class<?>)
+                type = (Class<?>) this.type;
+            else
+                this.type = type = getFieldType();
+            if (VerifyAccess.isTypeVisible(type, clazz))  return;
+            throw new LinkageError("bad field type alias: "+type+" not visible from "+clazz);
+        }
     }
 
+
     /** Produce a string form of this member name.
      *  For types, it is simply the type's own string (as reported by {@code toString}).
      *  For fields, it is {@code "DeclaringClass.name/type"}.
@@ -445,6 +693,7 @@
      *  If the declaring class is null, the prefix {@code "DeclaringClass."} is omitted.
      *  If the member is unresolved, a prefix {@code "*."} is prepended.
      */
+    @SuppressWarnings("LocalVariableHidesMemberVariable")
     @Override
     public String toString() {
         if (isType())
@@ -464,22 +713,12 @@
         } else {
             buf.append(type == null ? "(*)*" : getName(type));
         }
-        /*
-        buf.append('/');
-        // key: Public, private, pRotected, sTatic, Final, sYnchronized,
-        // transient/Varargs, native, (interface), abstract, sTrict, sYnthetic,
-        // (annotation), Enum, (unused)
-        final String FIELD_MOD_CHARS  = "PprTF?vt????Y?E?";
-        final String METHOD_MOD_CHARS = "PprTFybVn?atY???";
-        String modChars = (isInvocable() ? METHOD_MOD_CHARS : FIELD_MOD_CHARS);
-        for (int i = 0; i < modChars.length(); i++) {
-            if ((flags & (1 << i)) != 0) {
-                char mc = modChars.charAt(i);
-                if (mc != '?')
-                    buf.append(mc);
-            }
+        byte refKind = getReferenceKind();
+        if (refKind != REF_NONE) {
+            buf.append('/');
+            buf.append(MethodHandleNatives.refKindName(refKind));
         }
-         */
+        //buf.append("#").append(System.identityHashCode(this));
         return buf.toString();
     }
     private static String getName(Object obj) {
@@ -488,19 +727,6 @@
         return String.valueOf(obj);
     }
 
-    // Queries to the JVM:
-    /** Document? */
-    /*non-public*/ int getVMIndex() {
-        if (!isResolved())
-            throw newIllegalStateException("not resolved", this);
-        return vmindex;
-    }
-//    /*non-public*/ Object getVMTarget() {
-//        if (!isResolved())
-//            throw newIllegalStateException("not resolved", this);
-//        return vmtarget;
-//    }
-
     public IllegalAccessException makeAccessException(String message, Object from) {
         message = message + ": "+ toString();
         if (from != null)  message += ", from " + from;
@@ -518,14 +744,19 @@
     }
     public ReflectiveOperationException makeAccessException() {
         String message = message() + ": "+ toString();
-        if (isResolved())
-            return new IllegalAccessException(message);
+        ReflectiveOperationException ex;
+        if (isResolved() || !(resolution instanceof NoSuchMethodError ||
+                              resolution instanceof NoSuchFieldError))
+            ex = new IllegalAccessException(message);
         else if (isConstructor())
-            return new NoSuchMethodException(message);
+            ex = new NoSuchMethodException(message);
         else if (isMethod())
-            return new NoSuchMethodException(message);
+            ex = new NoSuchMethodException(message);
         else
-            return new NoSuchFieldException(message);
+            ex = new NoSuchFieldException(message);
+        if (resolution instanceof Throwable)
+            ex.initCause((Throwable) resolution);
+        return ex;
     }
 
     /** Actually making a query requires an access check. */
@@ -539,7 +770,7 @@
         private Factory() { } // singleton pattern
         static Factory INSTANCE = new Factory();
 
-        private static int ALLOWED_FLAGS = SEARCH_ALL_SUPERS | ALL_KINDS;
+        private static int ALLOWED_FLAGS = ALL_KINDS;
 
         /// Queries
         List<MemberName> getMembers(Class<?> defc,
@@ -573,14 +804,14 @@
                 // JVM returned to us with an intentional overflow!
                 totalCount += buf.length;
                 int excess = bufCount - buf.length;
-                if (bufs == null)  bufs = new ArrayList<MemberName[]>(1);
+                if (bufs == null)  bufs = new ArrayList<>(1);
                 bufs.add(buf);
                 int len2 = buf.length;
                 len2 = Math.max(len2, excess);
                 len2 = Math.max(len2, totalCount / 4);
                 buf = newMemberBuffer(Math.min(BUF_MAX, len2));
             }
-            ArrayList<MemberName> result = new ArrayList<MemberName>(totalCount);
+            ArrayList<MemberName> result = new ArrayList<>(totalCount);
             if (bufs != null) {
                 for (MemberName[] buf0 : bufs) {
                     Collections.addAll(result, buf0);
@@ -599,47 +830,29 @@
             }
             return result;
         }
-        boolean resolveInPlace(MemberName m, boolean searchSupers, Class<?> lookupClass) {
-            if (m.name == null || m.type == null) {  // find unique non-overloaded name
-                Class<?> defc = m.getDeclaringClass();
-                List<MemberName> choices = null;
-                if (m.isMethod())
-                    choices = getMethods(defc, searchSupers, m.name, (MethodType) m.type, lookupClass);
-                else if (m.isConstructor())
-                    choices = getConstructors(defc, lookupClass);
-                else if (m.isField())
-                    choices = getFields(defc, searchSupers, m.name, (Class<?>) m.type, lookupClass);
-                //System.out.println("resolving "+m+" to "+choices);
-                if (choices == null || choices.size() != 1)
-                    return false;
-                if (m.name == null)  m.name = choices.get(0).name;
-                if (m.type == null)  m.type = choices.get(0).type;
-            }
-            MethodHandleNatives.resolve(m, lookupClass);
-            if (m.isResolved())  return true;
-            int matchFlags = m.flags | (searchSupers ? SEARCH_ALL_SUPERS : 0);
-            String matchSig = m.getSignature();
-            MemberName[] buf = { m };
-            int n = MethodHandleNatives.getMembers(m.getDeclaringClass(),
-                    m.getName(), matchSig, matchFlags, lookupClass, 0, buf);
-            if (n == 0 || !m.isResolved())
-                return false;  // no result
-            else if (n == 1 || m.clazz.isInterface())
-                return true;   // unique result, or multiple inheritance is OK
-            else
-                return false;  // ambiguous result (can this happen?)
-        }
         /** Produce a resolved version of the given member.
          *  Super types are searched (for inherited members) if {@code searchSupers} is true.
          *  Access checking is performed on behalf of the given {@code lookupClass}.
          *  If lookup fails or access is not permitted, null is returned.
          *  Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
          */
-        public MemberName resolveOrNull(MemberName m, boolean searchSupers, Class<?> lookupClass) {
-            MemberName result = m.clone();
-            if (resolveInPlace(result, searchSupers, lookupClass))
-                return result;
-            return null;
+        private MemberName resolve(byte refKind, MemberName ref, Class<?> lookupClass) {
+            MemberName m = ref.clone();  // JVM will side-effect the ref
+            assert(refKind == m.getReferenceKind());
+            try {
+                m = MethodHandleNatives.resolve(m, lookupClass);
+                m.checkForTypeAlias();
+                m.resolution = null;
+            } catch (LinkageError ex) {
+                // JVM reports that the "bytecode behavior" would get an error
+                assert(!m.isResolved());
+                m.resolution = ex;
+                return m;
+            }
+            assert(m.referenceKindIsConsistent());
+            m.initResolved(true);
+            assert(m.vminfoIsConsistent());
+            return m;
         }
         /** Produce a resolved version of the given member.
          *  Super types are searched (for inherited members) if {@code searchSupers} is true.
@@ -649,16 +862,29 @@
          */
         public
         <NoSuchMemberException extends ReflectiveOperationException>
-        MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass,
+        MemberName resolveOrFail(byte refKind, MemberName m, Class<?> lookupClass,
                                  Class<NoSuchMemberException> nsmClass)
                 throws IllegalAccessException, NoSuchMemberException {
-            MemberName result = resolveOrNull(m, searchSupers, lookupClass);
-            if (result != null)
+            MemberName result = resolve(refKind, m, lookupClass);
+            if (result.isResolved())
                 return result;
-            ReflectiveOperationException ex = m.makeAccessException();
+            ReflectiveOperationException ex = result.makeAccessException();
             if (ex instanceof IllegalAccessException)  throw (IllegalAccessException) ex;
             throw nsmClass.cast(ex);
         }
+        /** Produce a resolved version of the given member.
+         *  Super types are searched (for inherited members) if {@code searchSupers} is true.
+         *  Access checking is performed on behalf of the given {@code lookupClass}.
+         *  If lookup fails or access is not permitted, return null.
+         *  Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
+         */
+        public
+        MemberName resolveOrNull(byte refKind, MemberName m, Class<?> lookupClass) {
+            MemberName result = resolve(refKind, m, lookupClass);
+            if (result.isResolved())
+                return result;
+            return null;
+        }
         /** Return a list of all methods defined by the given class.
          *  Super types are searched (for inherited members) if {@code searchSupers} is true.
          *  Access checking is performed on behalf of the given {@code lookupClass}.