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

Print this page

        

*** 73,98 **** * 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 */ ! @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. */ --- 73,93 ---- * 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 { 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 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(); // 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. */
*** 951,1022 **** && this.getReferenceKind() == that.getReferenceKind() && Objects.equals(this.name, that.name) && Objects.equals(this.getType(), that.getType()); } - @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. - */ - if (equals(that)) { - return 0; - } - - int diff = Integer.compare(this.name.hashCode(), that.name.hashCode()); - if (diff != 0) { - return diff; - } - - diff = this.getReferenceKind() - that.getReferenceKind(); - if (diff != 0) { - return diff; - } - - 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()); } --- 946,955 ----
*** 1356,1474 **** /** * 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); } } } --- 1289,1566 ---- /** * Lazily create {@link ClassData}. */ ! private static ClassData classData(Class<?> klazz) { ! ClassData classData = (ClassData) jla.getClassData(klazz); ! if (classData == null) { ! classData = new ClassData(); ! if (!jla.casClassData(klazz, null, classData)) { ! classData = (ClassData) jla.getClassData(klazz); ! } } ! return classData; } ! /* package */ static MemberName internMemberName(Class<?> klazz, MemberName memberName, int redefinedCount) { ! return classData(klazz).intern(klazz, memberName, redefinedCount); } /** * 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. This is a 'tail' pointer ! * to last element published to JVM. The chain of elements is ! * traversed using {@link MemberName#next} links until {@code null} ! * is reached. */ private volatile MemberName publishedToVM; ! ! /** ! * A linear-scan hash table used for interning. ! * The length of the array is always a power of 2, ! * greater than (1.5 * size) and less or equal to (3 * size) ! */ ! private volatile MemberName[] table; ! private int size; ! ! /** ! * The minimum capacity. ! * The value 2 corresponds to an expected maximum size of 1, ! * given a load factor of 2/3. MUST be a power of two. ! */ ! private static final int MINIMUM_CAPACITY = 2; ! ! /** ! * The maximum capacity. ! * <p/> ! * In fact, the table can hold no more than MAXIMUM_CAPACITY-1 elements ! * because it has to have at least one slot == null ! * in order to avoid infinite loops in get() and putIfAbsent(). ! */ ! private static final int MAXIMUM_CAPACITY = 1 << 30; /** * 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 redefinedCount 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 redefinedCount) { ! ! // check without holding lock ! MemberName internedMemberName = get(memberName); ! if (internedMemberName != null) { ! return internedMemberName; ! } ! ! // must hold lock now synchronized (this) { ! // This has to be a never published MemberName. ! assert memberName.next == null; ! ! // First attempt publication to JVM, if that succeeds, ! // then try to record in hash table. ! memberName.next = publishedToVM; ! publishedToVM = memberName; ! if (redefinedCount != jla.getClassRedefinedCount(klass)) { ! // Lost a race with JVM, back out publication and report failure. ! publishedToVM = memberName.next; ! // We can't set memberName.next to null here (without a write fence), ! // because the write could bubble above the write to publishedToVM! ! return null; ! } ! ! // Try to record in hash table. ! internedMemberName = putIfAbsent(memberName); ! if (internedMemberName != null) { ! // Lost race with interning, back out JVM publication ! // and return already interned element. ! publishedToVM = memberName.next; ! // We can't set memberName.next to null here (without a write fence), ! // because the write could bubble above the write to publishedToVM! ! return internedMemberName; } + + // Successfully recorded memberName, return it. + return memberName; } } ! ! /** ! * Lookup for an interned element. This method can be called without ! * any external synchronization. ! * ! * @param lookupElement the lookup object. ! * @return interned element if there is one or null if there is none. ! */ ! private MemberName get(MemberName lookupElement) { ! final MemberName[] tab = table; // volatile read ! if (tab == null) { ! return null; ! } ! int i = hash(lookupElement, tab.length); ! while (true) { ! MemberName element = getVolatile(tab, i); ! if (element == null) { ! return null; } ! if (element.equals(lookupElement)) { ! return element; ! } ! i = nextKeyIndex(i, tab.length); } } /** ! * Inserts new element if there is no interned element equal to it ! * already present. This method must be called under exclusive lock. * ! * @param newElement new element to insert. ! * @return null if new element was inserted or old element if ! * there was one (and no new element was inserted) ! */ ! private MemberName putIfAbsent(MemberName newElement) { ! if (newElement == null) throw new NullPointerException(); ! ! MemberName[] tab = table; // volatile read ! ! if (tab == null) { ! // lazily create initial table ! tab = new MemberName[MINIMUM_CAPACITY]; ! // still virgin ! tab[hash(newElement, tab.length)] = newElement; ! // publish initial table ! table = tab; ! size = 1; return null; } ! while (true) { ! ! int i = hash(newElement, tab.length); ! ! for (MemberName element; ! (element = tab[i]) != null; ! i = nextKeyIndex(i, tab.length)) { ! if (element.equals(newElement)) { ! // return existing element ! return element; ! } } ! final int newSize = size + 1; ! ! MemberName[] newTab; ! if ((newTab = resize(tab, newSize)) != null) { // we needed to resize ! // publish new table ! table = tab = newTab; ! // retry loop with new table ! } else { // current tab.length is enough ! // publish new element ! setVolatile(tab, i, newElement); ! size = newSize; ! return null; } } } + // utility methods + /** ! * Re-sizes the table if necessary to hold given number of elements. * ! * @return new table with elements copied to it if resizing was performed ! * or null if no resizing is necessary because the table ! * has enough room. ! */ ! private static MemberName[] resize(MemberName[] oldTable, int newSize) { ! int oldLength = oldTable.length; ! int newLength = oldLength; ! // length should always be >= 3 * size / 2. ! while (newLength < newSize + (newSize >> 1) && ! newLength < MAXIMUM_CAPACITY) { ! // next power of 2 ! newLength <<= 1; ! } ! if (oldLength == newLength) { // no resizing needed ! if (newSize == MAXIMUM_CAPACITY) ! throw new IllegalStateException("Capacity exhausted."); ! // current length is enough ! return null; ! } ! ! MemberName[] newTable = new MemberName[newLength]; ! ! // copy elements to new table ! for (MemberName element : oldTable) { ! if (element != null) { ! int i = hash(element, newLength); ! while (newTable[i] != null) ! i = nextKeyIndex(i, newLength); ! newTable[i] = element; ! } ! } ! ! return newTable; ! } ! ! /** ! * Returns index for Object x. */ ! private static int hash(Object x, int length) { ! int h = x.hashCode(); ! return (h ^ (h >>> 16)) & (length - 1); ! } ! ! /** ! * Circularly traverses table of size length (which is a power of 2). ! */ ! private static int nextKeyIndex(int i, int length) { ! return (i + 1) & (length - 1); ! } ! ! // Unsafe machinery ! private static final Unsafe UNSAFE = Unsafe.getUnsafe(); ! private static final long MN_ARRAY_BASE; ! private static final int MN_ARRAY_INDEX_SHIFT; ! ! static { ! try { ! MN_ARRAY_BASE = UNSAFE.arrayBaseOffset( ! MemberName[].class ! ); ! int scale = UNSAFE.arrayIndexScale(MemberName[].class); ! if ((scale & (scale - 1)) != 0) ! throw new Error("MemberName[] index scale not a power of two"); ! MN_ARRAY_INDEX_SHIFT = 31 - Integer.numberOfLeadingZeros(scale); ! } catch (Exception e) { ! throw new Error(e); ! } ! } ! ! private static MemberName getVolatile(MemberName[] tab, int i) { ! return (MemberName) UNSAFE.getObjectVolatile(tab, arrayOffset(tab, i)); ! } ! ! private static void setVolatile(MemberName[] tab, int i, MemberName element) { ! UNSAFE.putObjectVolatile(tab, arrayOffset(tab, i), element); ! } ! ! private static long arrayOffset(MemberName[] tab, int i) { ! if (i < 0 || i >= tab.length) ! throw new ArrayIndexOutOfBoundsException(i); ! return ((long) i << MN_ARRAY_INDEX_SHIFT) + MN_ARRAY_BASE; } } }