< prev index next >

src/java.base/share/classes/java/lang/invoke/MemberName.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2014, 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

@@ -36,53 +36,361 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
+import sun.misc.JavaLangAccess;
+import sun.misc.SharedSecrets;
+
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import static java.lang.invoke.MethodHandleStatics.*;
-import java.util.Objects;
+import sun.misc.Unsafe;
+
 
 /**
  * A {@code MemberName} is a compact symbolic datum which fully characterizes
  * a method or field reference.
  * A member name refers to a field, method, constructor, or member type.
  * Every member name has a simple name (a string) and a type (either a Class or MethodType).
  * A member name may also have a non-null declaring class, or it may be simply
  * a naked name/type pair.
  * A member name may also have non-zero modifier flags.
  * Finally, a member name may be either resolved or unresolved.
- * If it is resolved, the existence of the named
  * <p>
  * Whether resolved or not, a member name provides no access rights or
  * invocation capability to its possessor.  It is merely a compact
  * representation of all symbolic information necessary to link to
  * and properly use the named member.
  * <p>
  * When resolved, a member name's internal implementation may include references to JVM metadata.
  * This representation is stateless and only descriptive.
  * It provides no private information and no capability to use the member.
+ * Resolved MemberNames are always interned (except for Class MemberNames)
+ * which allows updating when classes are redefined.
  * <p>
  * By contrast, a {@linkplain java.lang.reflect.Method} contains fuller information
  * about the internals of a method (except its bytecodes) and also
  * allows invocation.  A MemberName is much lighter than a Method,
  * since it contains about 7 fields to the 16 of Method (plus its sub-arrays),
  * and those seven fields omit much of the information in Method.
  * @author jrose
  */
-/*non-public*/ final class MemberName implements Member, Cloneable {
+@SuppressWarnings("rawtypes") //Comparable in next line
+/*non-public*/ final class MemberName implements Member, Comparable, Cloneable {
     private Class<?> clazz;       // class in which the method is defined
     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 volatile MemberName next; // used for a linked list of MemberNames known to VM
     //@Injected JVM_Method* vmtarget;
     //@Injected int         vmindex;
     private Object   resolution;  // if null, this guy is resolved
 
+    private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
+    private static final Unsafe unsafe = Unsafe.getUnsafe();
+    static void storeFence() {
+        unsafe.storeFence();
+    }
+
+    // bare-bones constructor; the JVM will fill it in
+    MemberName() { }
+
+    /** 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(), REF_NONE));
+        initResolved(true);
+    }
+
+    // Construction from symbolic parts, for queries:
+    /** 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, 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, 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, byte refKind) {
+        int initFlags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
+        init(defClass, name, type, flagsMods(initFlags, 0, refKind));
+        initResolved(false);
+    }
+
+    /** Create a method, constructor, or field name from the given components:
+     *  Reference kind, declaring class, name, type.
+     */
+    public MemberName(byte refKind, Class<?> defClass, String name, Object type) {
+        int kindFlags;
+        if (MethodHandleNatives.refKindIsField(refKind)) {
+            kindFlags = IS_FIELD;
+            if (!(type instanceof Class))
+                throw newIllegalArgumentException("not a field type");
+        } else if (MethodHandleNatives.refKindIsMethod(refKind)) {
+            kindFlags = IS_METHOD;
+            if (!(type instanceof MethodType))
+                throw newIllegalArgumentException("not a method type");
+        } else if (refKind == REF_newInvokeSpecial) {
+            kindFlags = IS_CONSTRUCTOR;
+            if (!(type instanceof MethodType) ||
+                !CONSTRUCTOR_NAME.equals(name))
+                throw newIllegalArgumentException("not a constructor type or name");
+        } else {
+            throw newIllegalArgumentException("bad reference kind "+refKind);
+        }
+        init(defClass, name, type, flagsMods(kindFlags, 0, refKind));
+        initResolved(false);
+    }
+
+    /** Create a name for the given reflected method.  The resulting name will be in a resolved state. */
+    public static MemberName make(Method m) {
+        return make(m, false);
+    }
+
+    @SuppressWarnings("LeakingThisInConstructor")
+    private MemberName(Method m, boolean wantSpecial) {
+        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) {
+            if (isAbstract()) {
+                throw new AbstractMethodError(this.toString());
+            }
+            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 static MemberName make(Method m, boolean wantSpecial) {
+        // Unreflected member names are resolved so intern them here.
+        MemberName tmp0 = null;
+        InternTransaction tx = new InternTransaction(m.getDeclaringClass());
+        while (tmp0 == null) {
+            MemberName tmp = new MemberName(m, wantSpecial);
+            tmp0 = tx.tryIntern(tmp);
+        }
+        return tmp0;
+    }
+
+    @SuppressWarnings("LeakingThisInConstructor")
+    private MemberName(Constructor<?> ctor) {
+        ctor.getClass();  // NPE check
+        // fill in vmtarget, vmindex while we have ctor in hand:
+        MethodHandleNatives.init(this, ctor);
+        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 constructor.
+     * The resulting name will be in a resolved state.
+     */
+    public static MemberName make(Constructor<?> ctor) {
+        // Unreflected member names are resolved so intern them here.
+        MemberName tmp0 = null;
+        InternTransaction tx = new InternTransaction(ctor.getDeclaringClass());
+        while (tmp0 == null) {
+            MemberName tmp = new MemberName(ctor);
+            tmp0 = tx.tryIntern(tmp);
+        }
+        return tmp0;
+    }
+
+    /**
+     * Create a name for the given reflected field.
+     * The resulting name will be in a resolved state.
+     */
+    public static MemberName make(Field field) {
+        return make(field, false);
+    }
+
+    @SuppressWarnings("LeakingThisInConstructor")
+    private MemberName(Field field, boolean makeSetter) {
+        field.getClass();  // NPE check
+        // fill in vmtarget, vmindex while we have field in hand:
+        MethodHandleNatives.init(this, field);
+        assert(isResolved() && this.clazz != null);
+        this.name = field.getName();
+        this.type = field.getType();
+        byte refKind = this.getReferenceKind();
+        assert(refKind == (isStatic() ? REF_getStatic : REF_getField));
+        if (makeSetter) {
+            assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField));
+            changeReferenceKind((byte)(refKind + (REF_putStatic - REF_getStatic)), refKind);
+        }
+    }
+
+    /**
+     * Create a name for the given reflected field, optionally making it a setter.
+     * The resulting name will be in a resolved state.
+     */
+    public static MemberName make(Field field, boolean makeSetter) {
+        // Unreflected member names are resolved so intern them here.
+        MemberName tmp0 = null;
+        InternTransaction tx = new InternTransaction(field.getDeclaringClass());
+        while (tmp0 == null) {
+            MemberName tmp = new MemberName(field, makeSetter);
+            tmp0 = tx.tryIntern(tmp);
+        }
+        return tmp0;
+    }
+
+    /** 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)
+        //name.toString();  // null check
+        //type.equals(type);  // null check
+        // fill in fields:
+        this.clazz = defClass;
+        this.name = name;
+        this.type = type;
+        this.flags = flags;
+        assert(testAnyFlags(ALL_KINDS));
+        assert(this.resolution == null);  // nobody should have touched this yet
+        //assert(referenceKindIsConsistent());  // do this after resolution
+    }
+
+    private void initResolved(boolean isResolved) {
+        assert(this.resolution == null);  // not initialized yet!
+        if (!isResolved)
+            this.resolution = this;
+        assert(isResolved() == isResolved);
+    }
+
+    /**
+     * Helper method to intern this member name in the declaring class' member name table.
+     */
+    @SuppressWarnings("unchecked")
+    private MemberName intern(int transaction_token) {
+        return internMemberName(clazz, this, transaction_token);
+    }
+
+    /**
+     * Helper method to obtain the transaction token for interning, given Class clazz.
+     * @param clazz
+     * @return transaction token (class redefinition count)
+     */
+    static private int internTxnToken(Class clazz) {
+        return clazz == null ? 0 : jla.getClassRedefinedCount(clazz);
+    }
+
+    /**
+     * Helper method to obtain the transaction token for interning, given this.
+     * @return
+     */
+    private int internTxnToken() {
+        return internTxnToken(clazz);
+    }
+
+    /**
+     * Helper method to conditionally intern cloned+modified member names.
+     */
+    private MemberName intern(MemberName likeThis, int transaction_token) {
+        if (likeThis.isResolved())
+            return intern(transaction_token);
+        else
+            return this;
+    }
+
+    /**
+     * Helper class to handle the case where the class checked for no
+     * redefinition may not be correct; resolution can end up with a different
+     * class.
+     */
+    static class InternTransaction {
+        Class<?> tx_class;
+        int txn_token;
+
+        InternTransaction(Class<?> tx_class) {
+            this.tx_class = tx_class;
+            this.txn_token = internTxnToken(tx_class);
+        }
+
+        /**
+         * If member_name is not resolved, returns member_name; if is resolved,
+         * attempts to intern member_name (once) while checking for races.
+         *
+         * @param member_name
+         * @return member_name if not resolved, null if racing, otherwise
+         *         value of interned member_name.
+         */
+        MemberName tryIntern(MemberName member_name) {
+            if (member_name.isResolved()) {
+                if (member_name.clazz != tx_class) {
+                    Class prev_tx_class = tx_class;
+                    int prev_txn_token = txn_token;
+                    tx_class = member_name.clazz;
+                    txn_token = internTxnToken(tx_class);
+                    // Zero is a special case.
+                    if (txn_token != 0 ||
+                        prev_txn_token != internTxnToken(prev_tx_class)) {
+                        // Resolved class is different and at least one
+                        // redef of it occurred, therefore repeat with
+                        // proper class for race consistency checking.
+                        return null;
+                    }
+                }
+                member_name = member_name.intern(txn_token);
+                if (member_name == null) {
+                    // Update the token for the next try.
+                    txn_token = internTxnToken(tx_class);
+                }
+            }
+            return member_name;
+        }
+    }
+
     /** Return the declaring class of this member.
      *  In the case of a bare name and type, the declaring class will be null.
      */
+    @Override
     public Class<?> getDeclaringClass() {
         return clazz;
     }
 
     /** Utility method producing the class loader of the declaring class. */

@@ -93,10 +401,11 @@
     /** Return the simple name of this member.
      *  For a type, it is the same as {@link Class#getSimpleName}.
      *  For a method or field, it is the simple name of the member.
      *  For a constructor, it is always {@code "&lt;init&gt;"}.
      */
+    @Override
     public String getName() {
         if (name == null) {
             expandFromVM();
             if (name == null) {
                 return null;

@@ -238,10 +547,11 @@
     }
 
     /** Return the modifier flags of this member.
      *  @see java.lang.reflect.Modifier
      */
+    @Override
     public int getModifiers() {
         return (flags & RECOGNIZED_MODIFIERS);
     }
 
     /** Return the reference kind of this member, or zero if none.

@@ -415,10 +725,11 @@
     /** Utility method to query the modifier flags of this member; returns false if the member is not a method. */
     public boolean isVarargs() {
         return testAllFlags(VARARGS) && isInvocable();
     }
     /** Utility method to query the modifier flags of this member; returns false if the member is not a method. */
+    @Override
     public boolean isSynthetic() {
         return testAllFlags(SYNTHETIC);
     }
 
     static final String CONSTRUCTOR_NAME = "<init>";  // the ever-popular

@@ -477,25 +788,10 @@
     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)
-        //name.toString();  // null check
-        //type.equals(type);  // null check
-        // fill in fields:
-        this.clazz = defClass;
-        this.name = name;
-        this.type = type;
-        this.flags = flags;
-        assert(testAnyFlags(ALL_KINDS));
-        assert(this.resolution == null);  // nobody should have touched this yet
-        //assert(referenceKindIsConsistent());  // do this after resolution
-    }
-
     /**
      * Calls down to the VM to fill in the fields.  This method is
      * synchronized to avoid racing calls.
      */
     private void expandFromVM() {

@@ -513,62 +809,26 @@
         assert((flags & RECOGNIZED_MODIFIERS) == 0);
         assert((mods & ~RECOGNIZED_MODIFIERS) == 0);
         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) {
-        this(m, false);
-    }
-    @SuppressWarnings("LeakingThisInConstructor")
-    public MemberName(Method m, boolean wantSpecial) {
-        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) {
-            if (isAbstract())
-                throw new AbstractMethodError(this.toString());
-            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);
+        case REF_invokeVirtual:     return cloneAndChangeReferenceKindAndIntern(REF_invokeSpecial, REF_invokeVirtual);
+        case REF_invokeInterface:   return cloneAndChangeReferenceKindAndIntern(REF_invokeSpecial, REF_invokeInterface);
+        case REF_newInvokeSpecial:  return cloneAndChangeReferenceKindAndIntern(REF_invokeSpecial, REF_newInvokeSpecial);
         }
         throw new IllegalArgumentException(this.toString());
     }
     /** If this MN is not REF_newInvokeSpecial, return a clone with that ref. kind.
      *  In that case it must already be REF_invokeSpecial.
      */
     public MemberName asConstructor() {
         switch (getReferenceKind()) {
-        case REF_invokeSpecial:     return clone().changeReferenceKind(REF_newInvokeSpecial, REF_invokeSpecial);
+        case REF_invokeSpecial:     return cloneAndChangeReferenceKindAndIntern(REF_newInvokeSpecial, REF_invokeSpecial);
         case REF_newInvokeSpecial:  return this;
         }
         throw new IllegalArgumentException(this.toString());
     }
     /** If this MN is a REF_invokeSpecial, return a clone with the "normal" kind

@@ -591,45 +851,32 @@
             newRefKind = normalVirtual;
             break;
         }
         if (newRefKind == refKind)
             return this;
-        result = clone().changeReferenceKind(newRefKind, refKind);
+        result = cloneAndChangeReferenceKindAndIntern(newRefKind, refKind);
         assert(this.referenceKindIsConsistentWith(result.getReferenceKind()));
         return result;
     }
-    /** Create a name for the given reflected constructor.  The resulting name will be in a resolved state. */
-    @SuppressWarnings("LeakingThisInConstructor")
-    public MemberName(Constructor<?> ctor) {
-        ctor.getClass();  // NPE check
-        // fill in vmtarget, vmindex while we have ctor in hand:
-        MethodHandleNatives.init(this, ctor);
-        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.
-     */
-    public MemberName(Field fld) {
-        this(fld, false);
-    }
-    @SuppressWarnings("LeakingThisInConstructor")
-    public MemberName(Field fld, boolean makeSetter) {
-        fld.getClass();  // NPE check
-        // fill in vmtarget, vmindex while we have fld in hand:
-        MethodHandleNatives.init(this, fld);
-        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);
+
+    /**
+     * Clones, changes reference kind in the clone, and if this is resolved,
+     * also interns the result, repeating the intern operation till it succeeds.
+     *
+     * @param refKind
+     * @param oldKind
+     * @return
+     */
+    private MemberName cloneAndChangeReferenceKindAndIntern(byte refKind, byte oldKind) {
+        MemberName tmp0 = null;
+        while (tmp0 == null) {
+            int tx_token = internTxnToken(); // this and clone will have same token
+            tmp0 = clone().changeReferenceKind(refKind, oldKind).intern(this, tx_token);
         }
+        return tmp0;
     }
+
     public boolean isGetter() {
         return MethodHandleNatives.refKindIsGetter(getReferenceKind());
     }
     public boolean isSetter() {
         return MethodHandleNatives.refKindIsSetter(getReferenceKind());

@@ -637,23 +884,17 @@
     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(), REF_NONE));
-        initResolved(true);
+        return cloneAndChangeReferenceKindAndIntern(setterRefKind, refKind);
     }
-
     /**
      * 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.
+     * The returned MemberName is not resolved.
      * See comments on {@link MethodHandleNatives#linkMethod}.
      */
     static MemberName makeMethodHandleInvoke(String name, MethodType type) {
         return makeMethodHandleInvoke(name, type, MH_INVOKE_MODS | SYNTHETIC);
     }

@@ -662,13 +903,10 @@
         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() { }
-
     // locally useful cloner
     @Override protected MemberName clone() {
         try {
             return (MemberName) super.clone();
         } catch (CloneNotSupportedException ex) {

@@ -713,56 +951,72 @@
                 && this.getReferenceKind() == that.getReferenceKind()
                 && 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, 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.
+    @Override
+    public int compareTo(Object o) {
+        MemberName that = (MemberName) o;
+
+        /* First test equals.  This make the ordering checks easier because we
+         * don't have to be precise and can use hash codes.
      */
-    public MemberName(Class<?> defClass, String name, Class<?> type, byte refKind) {
-        init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind));
-        initResolved(false);
+        if (equals(that)) {
+            return 0;
     }
-    /** Create a method or constructor name from the given components:
-     *  Declaring class, name, type, reference kind.
-     *  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, byte refKind) {
-        int initFlags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
-        init(defClass, name, type, flagsMods(initFlags, 0, refKind));
-        initResolved(false);
+
+        int diff = Integer.compare(this.name.hashCode(), that.name.hashCode());
+        if (diff != 0) {
+            return diff;
     }
-    /** Create a method, constructor, or field name from the given components:
-     *  Reference kind, declaring class, name, type.
-     */
-    public MemberName(byte refKind, Class<?> defClass, String name, Object type) {
-        int kindFlags;
-        if (MethodHandleNatives.refKindIsField(refKind)) {
-            kindFlags = IS_FIELD;
-            if (!(type instanceof Class))
-                throw newIllegalArgumentException("not a field type");
-        } else if (MethodHandleNatives.refKindIsMethod(refKind)) {
-            kindFlags = IS_METHOD;
-            if (!(type instanceof MethodType))
-                throw newIllegalArgumentException("not a method type");
-        } else if (refKind == REF_newInvokeSpecial) {
-            kindFlags = IS_CONSTRUCTOR;
-            if (!(type instanceof MethodType) ||
-                !CONSTRUCTOR_NAME.equals(name))
-                throw newIllegalArgumentException("not a constructor type or name");
-        } else {
-            throw newIllegalArgumentException("bad reference kind "+refKind);
+
+        diff = this.getReferenceKind() - that.getReferenceKind();
+        if (diff != 0) {
+            return diff;
         }
-        init(defClass, name, type, flagsMods(kindFlags, 0, refKind));
-        initResolved(false);
+
+        diff = Integer.compare(this.getType().hashCode(), that.getType().hashCode());
+        if (diff != 0) {
+            return diff;
+        }
+
+        // Hashcodes apparently collided, try more detail.
+        diff = this.name.compareTo(that.name);
+        if (diff != 0) {
+            return diff;
+        }
+
+        // The classes ought to all be equal anyway in the usual use of
+        // compareTo, so check these later,
+        // but before comparing getType.toString().
+        diff = Integer.compare(this.clazz.hashCode(),that.clazz.hashCode());
+        if (diff != 0) {
+            return diff;
+        }
+
+        diff = this.clazz.getName().compareTo(that.clazz.getName());
+        if (diff != 0) {
+            return diff;
     }
+
+        diff = this.getType().toString().compareTo(that.getType().toString());
+        if (diff != 0) {
+            return diff;
+        }
+
+        // Nothing left but classLoaders.
+        ClassLoader thisCl = this.getClassLoader();
+        ClassLoader thatCl = that.getClassLoader();
+
+        if (thisCl == thatCl) return 0;
+
+        diff = Integer.compare(thisCl == null ? 0 : thisCl.hashCode(),
+                               thatCl == null ? 0 : thatCl.hashCode());
+
+        return diff;
+    }
+
     /** Query whether this member name is resolved to a non-static, non-final method.
      */
     public boolean hasReceiverTypeDispatch() {
         return MethodHandleNatives.refKindDoesDispatch(getReferenceKind());
     }

@@ -774,17 +1028,10 @@
      */
     public boolean isResolved() {
         return resolution == null;
     }
 
-    private void initResolved(boolean isResolved) {
-        assert(this.resolution == null);  // not initialized yet!
-        if (!isResolved)
-            this.resolution = this;
-        assert(isResolved() == isResolved);
-    }
-
     void checkForTypeAlias() {
         if (isInvocable()) {
             MethodType type;
             if (this.type instanceof MethodType)
                 type = (MethodType) this.type;

@@ -887,11 +1134,11 @@
      */
     /*non-public*/ static class Factory {
         private Factory() { } // singleton pattern
         static Factory INSTANCE = new Factory();
 
-        private static int ALLOWED_FLAGS = ALL_KINDS;
+        private final static int ALLOWED_FLAGS = ALL_KINDS;
 
         /// Queries
         List<MemberName> getMembers(Class<?> defc,
                 String matchName, Object matchType,
                 int matchFlags, Class<?> lookupClass) {

@@ -953,11 +1200,11 @@
          *  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.
          */
-        private MemberName resolve(byte refKind, MemberName ref, Class<?> lookupClass) {
+        private static 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();

@@ -969,43 +1216,80 @@
                 return m;
             }
             assert(m.referenceKindIsConsistent());
             m.initResolved(true);
             assert(m.vminfoIsConsistent());
+            // m = m.intern(); // do not intern -- caller should intern instead.
             return m;
         }
         /** Produce a resolved version of the given member.
          *  Super types are searched (for inherited members) if {@code searchSupers} is true.
          *  Access checking is performed on behalf of the given {@code lookupClass}.
          *  If lookup fails or access is not permitted, a {@linkplain ReflectiveOperationException} is thrown.
-         *  Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
+         *  Otherwise the given member or its interned copy is returned, with modifier bits filled in.
          */
         public
         <NoSuchMemberException extends ReflectiveOperationException>
         MemberName resolveOrFail(byte refKind, MemberName m, Class<?> lookupClass,
                                  Class<NoSuchMemberException> nsmClass)
                 throws IllegalAccessException, NoSuchMemberException {
-            MemberName result = resolve(refKind, m, lookupClass);
-            if (result.isResolved())
+            MemberName result = resolveOrNot(refKind, m, lookupClass);
+
+            if (result.isResolved()) {
                 return result;
+            } else { // not resolved, throw exception
             ReflectiveOperationException ex = result.makeAccessException();
-            if (ex instanceof IllegalAccessException)  throw (IllegalAccessException) ex;
+                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.
+         *  Otherwise the given member or its interned copy 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())
+            MemberName result = resolveOrNot(refKind, m, lookupClass);
+            return result.isResolved() ? result : null;
+        }
+
+        private static
+        MemberName resolveOrNot(byte refKind, MemberName m, Class<?> lookupClass) {
+            MemberName result = null;
+            Class tx_class = m.clazz; // Guess a class for race detection.
+            while (result == null) { // iterate till no race.
+                int txn_token = internTxnToken(tx_class);
+
+                MemberName result0 = resolve(refKind, m, lookupClass);
+
+                if (result0.isResolved()) {
+                    if (result0.clazz != tx_class) { // Confirm/correct guess
+                        Class prev_tx_class = tx_class;
+                        int prev_txn_token = txn_token;
+                        tx_class = result0.clazz;
+                        txn_token = internTxnToken(tx_class);
+                        // The guess may be good enough
+                        if (txn_token != 0 ||
+                            prev_txn_token != internTxnToken(prev_tx_class)) {
+                            // Resolved class is different and at least one
+                            // redef occurred, therefore repeat with
+                            // proper class for race consistency checking.
+                            continue;
+                        }
+                    }
+                    result = result0.intern(txn_token);
+                } else { // Resolution failed, do not intern, return unresolved MemberName
+                    return result0;
+                }
+            }
                 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}.
          *  Inaccessible members are not added to the last.
          */

@@ -1068,10 +1352,123 @@
                 buf[i] = new MemberName();
             return buf;
         }
     }
 
-//    static {
-//        System.out.println("Hello world!  My methods are:");
-//        System.out.println(Factory.INSTANCE.getMethods(MemberName.class, true, null));
-//    }
+
+    /**
+     * Lazily create {@link ClassData}.
+     */
+    /* package */ static ClassData classData(Class<?> klazz) {
+        if (jla.getClassData(klazz) == null) {
+            jla.casClassData(klazz, null, new ClassData());
+        }
+        return (ClassData) jla.getClassData(klazz);
+    }
+
+    /* package */ static MemberName internMemberName(Class<?> klazz, MemberName memberName, int redefined_count) {
+        return classData(klazz).intern(klazz, memberName, redefined_count);
+    }
+
+    /**
+     * ClassData
+     */
+    private static class ClassData {
+        /**
+         * This needs to be a simple data structure because we need to access
+         * and update its elements from the JVM.  Note that the Java side controls
+         * the allocation and order of elements in the array; the JVM modifies
+         * fields of those elements during class redefinition.
+         */
+        private volatile MemberName[] elementData;
+        private volatile MemberName publishedToVM;
+        private volatile int size;
+
+        /**
+         * Interns a member name in the member name table.
+         * Returns null if a race with the jvm occurred.  Races are detected
+         * by checking for changes in the class redefinition count that occur
+         * before an intern is complete.
+         *
+         * @param klass class whose redefinition count is checked.
+         * @param memberName member name to be interned
+         * @param redefined_count the value of classRedefinedCount() observed before
+         *                         creation of the MemberName that is being interned.
+         * @return null if a race occurred, otherwise the interned MemberName.
+         */
+        @SuppressWarnings({"unchecked","rawtypes"})
+        public MemberName intern(Class<?> klass, MemberName memberName, int redefined_count) {
+            if (elementData == null) {
+                synchronized (this) {
+                    if (elementData == null) {
+                        elementData = new MemberName[1];
+                    }
+                }
+            }
+            synchronized (this) { // this == ClassData
+                final int index = Arrays.binarySearch(elementData, 0, size, memberName);
+                if (index >= 0) {
+                    return elementData[index];
+                }
+                // Not found, add carefully.
+                return add(klass, ~index, memberName, redefined_count);
+            }
+        }
+
+        /**
+         * Appends the specified element at the specified index.
+         *
+         * @param klass the klass for this ClassData.
+         * @param index index at which insertion should occur
+         * @param e element to be insert into this list
+         * @param redefined_count value of klass.classRedefinedCount() before e was created.
+         * @return null if insertion failed because of redefinition race, otherwise e.
+         */
+        private MemberName add(Class<?> klass, int index, MemberName e, int redefined_count) {
+            // First attempt publication to JVM, if that succeeds,
+            // then record internally.
+            e.next = publishedToVM;
+            publishedToVM = e;
+            storeFence();
+            if (redefined_count != jla.getClassRedefinedCount(klass)) {
+                // Lost a race, back out publication and report failure.
+                publishedToVM = e.next;
+                return null;
+            }
+
+            int oldSize = size;
+            MemberName[] element_data = elementData;
+            if (oldSize + 1 > element_data.length ) {
+                grow(oldSize + 1);
+                element_data = elementData;
+            }
+
+            if (oldSize > 0) {
+                for (int i = oldSize; i > index; i--) {
+                    // pre: element_data[i] is duplicated at [i+1]
+                    element_data[i] = element_data[i - 1];
+                    // post: element_data[i-1] is duplicated at [i]
+                }
+                // element_data[index] is duplicated at [index+1]
+            }
+            element_data[index] = e;
+            size += 1;
+            return e;
+        }
+
+        /**
+         * Increases the capacity to ensure that it can hold at least the
+         * number of elements specified by the minimum capacity argument.
+         *
+         * @param minCapacity the desired minimum capacity
+         */
+        private void grow(int minCapacity) {
+            // overflow-conscious code
+            int oldCapacity = elementData.length;
+            int newCapacity = oldCapacity + (oldCapacity >> 1);
+            if (newCapacity - minCapacity < 0)
+                newCapacity = minCapacity;
+            // minCapacity is usually close to size, so this is a win:
+            elementData = Arrays.copyOf(elementData, newCapacity);
+        }
+    }
 }
< prev index next >