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

Print this page




  58  * Finally, a member name may be either resolved or unresolved.
  59  * <p>
  60  * Whether resolved or not, a member name provides no access rights or
  61  * invocation capability to its possessor.  It is merely a compact
  62  * representation of all symbolic information necessary to link to
  63  * and properly use the named member.
  64  * <p>
  65  * When resolved, a member name's internal implementation may include references to JVM metadata.
  66  * This representation is stateless and only descriptive.
  67  * It provides no private information and no capability to use the member.
  68  * Resolved MemberNames are always interned (except for Class MemberNames)
  69  * which allows updating when classes are redefined.
  70  * <p>
  71  * By contrast, a {@linkplain java.lang.reflect.Method} contains fuller information
  72  * about the internals of a method (except its bytecodes) and also
  73  * allows invocation.  A MemberName is much lighter than a Method,
  74  * since it contains about 7 fields to the 16 of Method (plus its sub-arrays),
  75  * and those seven fields omit much of the information in Method.
  76  * @author jrose
  77  */
  78 @SuppressWarnings("rawtypes") //Comparable in next line
  79 /*non-public*/ final class MemberName implements Member, Comparable, Cloneable {
  80     private Class<?> clazz;       // class in which the method is defined
  81     private String   name;        // may be null if not yet materialized
  82     private Object   type;        // may be null if not yet materialized
  83     private int      flags;       // modifier bits; see reflect.Modifier
  84     private volatile MemberName next; // used for a linked list of MemberNames known to VM
  85     //@Injected JVM_Method* vmtarget;
  86     //@Injected int         vmindex;
  87     private Object   resolution;  // if null, this guy is resolved
  88 
  89     private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
  90     private static final Unsafe unsafe = Unsafe.getUnsafe();
  91     static void storeFence() {
  92         unsafe.storeFence();
  93     }
  94 
  95     // bare-bones constructor; the JVM will fill it in
  96     MemberName() { }
  97 
  98     /** Create a name for the given class.  The resulting name will be in a resolved state. */
  99     public MemberName(Class<?> type) {
 100         init(type.getDeclaringClass(), type.getSimpleName(), type,
 101                 flagsMods(IS_TYPE, type.getModifiers(), REF_NONE));
 102         initResolved(true);
 103     }
 104 
 105     // Construction from symbolic parts, for queries:
 106     /** Create a field or type name from the given components:
 107      *  Declaring class, name, type, reference kind.
 108      *  The declaring class may be supplied as null if this is to be a bare name and type.
 109      *  The resulting name will in an unresolved state.
 110      */
 111     public MemberName(Class<?> defClass, String name, Class<?> type, byte refKind) {
 112         init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind));
 113         initResolved(false);


 936     }
 937     @Override
 938     public boolean equals(Object that) {
 939         return (that instanceof MemberName && this.equals((MemberName)that));
 940     }
 941 
 942     /** Decide if two member names have exactly the same symbolic content.
 943      *  Does not take into account any actual class members, so even if
 944      *  two member names resolve to the same actual member, they may
 945      *  be distinct references.
 946      */
 947     public boolean equals(MemberName that) {
 948         if (this == that)  return true;
 949         if (that == null)  return false;
 950         return this.clazz == that.clazz
 951                 && this.getReferenceKind() == that.getReferenceKind()
 952                 && Objects.equals(this.name, that.name)
 953                 && Objects.equals(this.getType(), that.getType());
 954     }
 955 
 956     @Override
 957     public int compareTo(Object o) {
 958         MemberName that = (MemberName) o;
 959 
 960         /* First test equals.  This make the ordering checks easier because we
 961          * don't have to be precise and can use hash codes.
 962          */
 963         if (equals(that)) {
 964             return 0;
 965         }
 966 
 967         int diff = Integer.compare(this.name.hashCode(), that.name.hashCode());
 968         if (diff != 0) {
 969             return diff;
 970         }
 971 
 972         diff = this.getReferenceKind() - that.getReferenceKind();
 973         if (diff != 0) {
 974             return diff;
 975         }
 976 
 977         diff = Integer.compare(this.getType().hashCode(), that.getType().hashCode());
 978         if (diff != 0) {
 979             return diff;
 980         }
 981 
 982         // Hashcodes apparently collided, try more detail.
 983         diff = this.name.compareTo(that.name);
 984         if (diff != 0) {
 985             return diff;
 986         }
 987 
 988         // The classes ought to all be equal anyway in the usual use of
 989         // compareTo, so check these later,
 990         // but before comparing getType.toString().
 991         diff = Integer.compare(this.clazz.hashCode(),that.clazz.hashCode());
 992         if (diff != 0) {
 993             return diff;
 994         }
 995 
 996         diff = this.clazz.getName().compareTo(that.clazz.getName());
 997         if (diff != 0) {
 998             return diff;
 999         }
1000 
1001         diff = this.getType().toString().compareTo(that.getType().toString());
1002         if (diff != 0) {
1003             return diff;
1004         }
1005 
1006         // Nothing left but classLoaders.
1007         ClassLoader thisCl = this.getClassLoader();
1008         ClassLoader thatCl = that.getClassLoader();
1009 
1010         if (thisCl == thatCl) return 0;
1011 
1012         diff = Integer.compare(thisCl == null ? 0 : thisCl.hashCode(),
1013                                thatCl == null ? 0 : thatCl.hashCode());
1014 
1015         return diff;
1016     }
1017 
1018     /** Query whether this member name is resolved to a non-static, non-final method.
1019      */
1020     public boolean hasReceiverTypeDispatch() {
1021         return MethodHandleNatives.refKindDoesDispatch(getReferenceKind());
1022     }
1023 
1024     /** Query whether this member name is resolved.
1025      *  A resolved member name is one for which the JVM has found
1026      *  a method, constructor, field, or type binding corresponding exactly to the name.
1027      *  (Document?)
1028      */
1029     public boolean isResolved() {
1030         return resolution == null;
1031     }
1032 
1033     void checkForTypeAlias() {
1034         if (isInvocable()) {
1035             MethodType type;
1036             if (this.type instanceof MethodType)
1037                 type = (MethodType) this.type;


1341          *  Inaccessible members are not added to the last.
1342          */
1343         public List<MemberName> getNestedTypes(Class<?> defc, boolean searchSupers,
1344                 Class<?> lookupClass) {
1345             int matchFlags = IS_TYPE | (searchSupers ? SEARCH_ALL_SUPERS : 0);
1346             return getMembers(defc, null, null, matchFlags, lookupClass);
1347         }
1348         private static MemberName[] newMemberBuffer(int length) {
1349             MemberName[] buf = new MemberName[length];
1350             // fill the buffer with dummy structs for the JVM to fill in
1351             for (int i = 0; i < length; i++)
1352                 buf[i] = new MemberName();
1353             return buf;
1354         }
1355     }
1356 
1357 
1358     /**
1359      * Lazily create {@link ClassData}.
1360      */
1361     /* package */ static ClassData classData(Class<?> klazz) {
1362         if (jla.getClassData(klazz) == null) {
1363             jla.casClassData(klazz, null, new ClassData());




1364         }
1365         return (ClassData) jla.getClassData(klazz);
1366     }
1367 
1368     /* package */ static MemberName internMemberName(Class<?> klazz, MemberName memberName, int redefined_count) {
1369         return classData(klazz).intern(klazz, memberName, redefined_count);
1370     }
1371 
1372     /**
1373      * ClassData
1374      */
1375     private static class ClassData {
1376         /**
1377          * This needs to be a simple data structure because we need to access
1378          * and update its elements from the JVM.  Note that the Java side controls
1379          * the allocation and order of elements in the array; the JVM modifies
1380          * fields of those elements during class redefinition.

1381          */
1382         private volatile MemberName[] elementData;
1383         private volatile MemberName publishedToVM;
1384         private volatile int size;























1385 
1386         /**
1387          * Interns a member name in the member name table.
1388          * Returns null if a race with the jvm occurred.  Races are detected
1389          * by checking for changes in the class redefinition count that occur
1390          * before an intern is complete.
1391          *
1392          * @param klass class whose redefinition count is checked.
1393          * @param memberName member name to be interned
1394          * @param redefined_count the value of classRedefinedCount() observed before
1395          *                         creation of the MemberName that is being interned.
1396          * @return null if a race occurred, otherwise the interned MemberName.
1397          */
1398         @SuppressWarnings({"unchecked","rawtypes"})
1399         public MemberName intern(Class<?> klass, MemberName memberName, int redefined_count) {
1400             if (elementData == null) {







1401                 synchronized (this) {
1402                     if (elementData == null) {
1403                         elementData = new MemberName[1];






















1404                     }



1405                 }
1406             }
1407             synchronized (this) { // this == ClassData
1408                 final int index = Arrays.binarySearch(elementData, 0, size, memberName);
1409                 if (index >= 0) {
1410                     return elementData[index];














1411                 }
1412                 // Not found, add carefully.
1413                 return add(klass, ~index, memberName, redefined_count);


1414             }
1415         }
1416 
1417         /**
1418          * Appends the specified element at the specified index.

1419          *
1420          * @param klass the klass for this ClassData.
1421          * @param index index at which insertion should occur
1422          * @param e element to be insert into this list
1423          * @param redefined_count value of klass.classRedefinedCount() before e was created.
1424          * @return null if insertion failed because of redefinition race, otherwise e.
1425          */
1426         private MemberName add(Class<?> klass, int index, MemberName e, int redefined_count) {
1427             // First attempt publication to JVM, if that succeeds,
1428             // then record internally.
1429             e.next = publishedToVM;
1430             publishedToVM = e;
1431             storeFence();
1432             if (redefined_count != jla.getClassRedefinedCount(klass)) {
1433                 // Lost a race, back out publication and report failure.
1434                 publishedToVM = e.next;


1435                 return null;
1436             }
1437 
1438             int oldSize = size;
1439             MemberName[] element_data = elementData;
1440             if (oldSize + 1 > element_data.length ) {
1441                 grow(oldSize + 1);
1442                 element_data = elementData;






1443             }
1444 
1445             if (oldSize > 0) {
1446                 for (int i = oldSize; i > index; i--) {
1447                     // pre: element_data[i] is duplicated at [i+1]
1448                     element_data[i] = element_data[i - 1];
1449                     // post: element_data[i-1] is duplicated at [i]







1450                 }
1451                 // element_data[index] is duplicated at [index+1]
1452             }
1453             element_data[index] = e;
1454             size += 1;
1455             return e;
1456         }
1457 


1458         /**
1459          * Increases the capacity to ensure that it can hold at least the
1460          * number of elements specified by the minimum capacity argument.
1461          *
1462          * @param minCapacity the desired minimum capacity




































1463          */
1464         private void grow(int minCapacity) {
1465             // overflow-conscious code
1466             int oldCapacity = elementData.length;
1467             int newCapacity = oldCapacity + (oldCapacity >> 1);
1468             if (newCapacity - minCapacity < 0)
1469                 newCapacity = minCapacity;
1470             // minCapacity is usually close to size, so this is a win:
1471             elementData = Arrays.copyOf(elementData, newCapacity);



































1472         }
1473     }
1474 }


  58  * Finally, a member name may be either resolved or unresolved.
  59  * <p>
  60  * Whether resolved or not, a member name provides no access rights or
  61  * invocation capability to its possessor.  It is merely a compact
  62  * representation of all symbolic information necessary to link to
  63  * and properly use the named member.
  64  * <p>
  65  * When resolved, a member name's internal implementation may include references to JVM metadata.
  66  * This representation is stateless and only descriptive.
  67  * It provides no private information and no capability to use the member.
  68  * Resolved MemberNames are always interned (except for Class MemberNames)
  69  * which allows updating when classes are redefined.
  70  * <p>
  71  * By contrast, a {@linkplain java.lang.reflect.Method} contains fuller information
  72  * about the internals of a method (except its bytecodes) and also
  73  * allows invocation.  A MemberName is much lighter than a Method,
  74  * since it contains about 7 fields to the 16 of Method (plus its sub-arrays),
  75  * and those seven fields omit much of the information in Method.
  76  * @author jrose
  77  */
  78 /*non-public*/ final class MemberName implements Member, Cloneable {

  79     private Class<?>   clazz;       // class in which the method is defined
  80     private String     name;        // may be null if not yet materialized
  81     private Object     type;        // may be null if not yet materialized
  82     private int        flags;       // modifier bits; see reflect.Modifier
  83     private MemberName next;        // used for a linked list of MemberNames known to VM
  84     //@Injected JVM_Method* vmtarget;
  85     //@Injected int         vmindex;
  86     private Object     resolution;  // if null, this guy is resolved
  87 
  88     private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();




  89 
  90     // bare-bones constructor; the JVM will fill it in
  91     MemberName() { }
  92 
  93     /** Create a name for the given class.  The resulting name will be in a resolved state. */
  94     public MemberName(Class<?> type) {
  95         init(type.getDeclaringClass(), type.getSimpleName(), type,
  96                 flagsMods(IS_TYPE, type.getModifiers(), REF_NONE));
  97         initResolved(true);
  98     }
  99 
 100     // Construction from symbolic parts, for queries:
 101     /** Create a field or type name from the given components:
 102      *  Declaring class, name, type, reference kind.
 103      *  The declaring class may be supplied as null if this is to be a bare name and type.
 104      *  The resulting name will in an unresolved state.
 105      */
 106     public MemberName(Class<?> defClass, String name, Class<?> type, byte refKind) {
 107         init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind));
 108         initResolved(false);


 931     }
 932     @Override
 933     public boolean equals(Object that) {
 934         return (that instanceof MemberName && this.equals((MemberName)that));
 935     }
 936 
 937     /** Decide if two member names have exactly the same symbolic content.
 938      *  Does not take into account any actual class members, so even if
 939      *  two member names resolve to the same actual member, they may
 940      *  be distinct references.
 941      */
 942     public boolean equals(MemberName that) {
 943         if (this == that)  return true;
 944         if (that == null)  return false;
 945         return this.clazz == that.clazz
 946                 && this.getReferenceKind() == that.getReferenceKind()
 947                 && Objects.equals(this.name, that.name)
 948                 && Objects.equals(this.getType(), that.getType());
 949     }
 950 






























































 951     /** Query whether this member name is resolved to a non-static, non-final method.
 952      */
 953     public boolean hasReceiverTypeDispatch() {
 954         return MethodHandleNatives.refKindDoesDispatch(getReferenceKind());
 955     }
 956 
 957     /** Query whether this member name is resolved.
 958      *  A resolved member name is one for which the JVM has found
 959      *  a method, constructor, field, or type binding corresponding exactly to the name.
 960      *  (Document?)
 961      */
 962     public boolean isResolved() {
 963         return resolution == null;
 964     }
 965 
 966     void checkForTypeAlias() {
 967         if (isInvocable()) {
 968             MethodType type;
 969             if (this.type instanceof MethodType)
 970                 type = (MethodType) this.type;


1274          *  Inaccessible members are not added to the last.
1275          */
1276         public List<MemberName> getNestedTypes(Class<?> defc, boolean searchSupers,
1277                 Class<?> lookupClass) {
1278             int matchFlags = IS_TYPE | (searchSupers ? SEARCH_ALL_SUPERS : 0);
1279             return getMembers(defc, null, null, matchFlags, lookupClass);
1280         }
1281         private static MemberName[] newMemberBuffer(int length) {
1282             MemberName[] buf = new MemberName[length];
1283             // fill the buffer with dummy structs for the JVM to fill in
1284             for (int i = 0; i < length; i++)
1285                 buf[i] = new MemberName();
1286             return buf;
1287         }
1288     }
1289 
1290 
1291     /**
1292      * Lazily create {@link ClassData}.
1293      */
1294     private static ClassData classData(Class<?> klazz) {
1295         ClassData classData = (ClassData) jla.getClassData(klazz);
1296         if (classData == null) {
1297             classData = new ClassData();
1298             if (!jla.casClassData(klazz, null, classData)) {
1299                 classData = (ClassData) jla.getClassData(klazz);
1300             }
1301         }
1302         return classData;
1303     }
1304 
1305     /* package */ static MemberName internMemberName(Class<?> klazz, MemberName memberName, int redefinedCount) {
1306         return classData(klazz).intern(klazz, memberName, redefinedCount);
1307     }
1308 
1309     /**
1310      * ClassData
1311      */
1312     private static class ClassData {
1313         /**
1314          * This needs to be a simple data structure because we need to access
1315          * and update its elements from the JVM. This is a 'tail' pointer
1316          * to last element published to JVM. The chain of elements is
1317          * traversed using {@link MemberName#next} links until {@code null}
1318          * is reached.
1319          */

1320         private volatile MemberName publishedToVM;
1321 
1322         /**
1323          * A linear-scan hash table used for interning.
1324          * The length of the array is always a power of 2,
1325          * greater than (1.5 * size) and less or equal to (3 * size)
1326          */
1327         private volatile MemberName[] table;
1328         private int size;
1329 
1330         /**
1331          * The minimum capacity.
1332          * The value 2 corresponds to an expected maximum size of 1,
1333          * given a load factor of 2/3. MUST be a power of two.
1334          */
1335         private static final int MINIMUM_CAPACITY = 2;
1336 
1337         /**
1338          * The maximum capacity.
1339          * <p/>
1340          * In fact, the table can hold no more than MAXIMUM_CAPACITY-1 elements
1341          * because it has to have at least one slot == null
1342          * in order to avoid infinite loops in get() and putIfAbsent().
1343          */
1344         private static final int MAXIMUM_CAPACITY = 1 << 30;
1345 
1346         /**
1347          * Interns a member name in the member name table.
1348          * Returns null if a race with the jvm occurred.  Races are detected
1349          * by checking for changes in the class redefinition count that occur
1350          * before an intern is complete.
1351          *
1352          * @param klass          class whose redefinition count is checked.
1353          * @param memberName     member name to be interned
1354          * @param redefinedCount the value of classRedefinedCount() observed before
1355          *                       creation of the MemberName that is being interned.
1356          * @return null if a race occurred, otherwise the interned MemberName.
1357          */
1358         @SuppressWarnings({"unchecked", "rawtypes"})
1359         public MemberName intern(Class<?> klass, MemberName memberName, int redefinedCount) {
1360 
1361             // check without holding lock
1362             MemberName internedMemberName = get(memberName);
1363             if (internedMemberName != null) {
1364                 return internedMemberName;
1365             }
1366 
1367             // must hold lock now
1368             synchronized (this) {
1369                 // This has to be a never published MemberName.
1370                 assert memberName.next == null;
1371 
1372                 // First attempt publication to JVM, if that succeeds,
1373                 // then try to record in hash table.
1374                 memberName.next = publishedToVM;
1375                 publishedToVM = memberName;
1376                 if (redefinedCount != jla.getClassRedefinedCount(klass)) {
1377                     // Lost a race with JVM, back out publication and report failure.
1378                     publishedToVM = memberName.next;
1379                     // We can't set memberName.next to null here (without a write fence),
1380                     // because the write could bubble above the write to publishedToVM!
1381                     return null;
1382                 }
1383 
1384                 // Try to record in hash table.
1385                 internedMemberName = putIfAbsent(memberName);
1386                 if (internedMemberName != null) {
1387                     // Lost race with interning, back out JVM publication
1388                     // and return already interned element.
1389                     publishedToVM = memberName.next;
1390                     // We can't set memberName.next to null here (without a write fence),
1391                     // because the write could bubble above the write to publishedToVM!
1392                     return internedMemberName;
1393                 }
1394 
1395                 // Successfully recorded memberName, return it.
1396                 return memberName;
1397             }
1398         }
1399 
1400         /**
1401          * Lookup for an interned element. This method can be called without
1402          * any external synchronization.
1403          *
1404          * @param lookupElement the lookup object.
1405          * @return interned element if there is one or null if there is none.
1406          */
1407         private MemberName get(MemberName lookupElement) {
1408             final MemberName[] tab = table; // volatile read
1409             if (tab == null) {
1410                 return null;
1411             }
1412             int i = hash(lookupElement, tab.length);
1413             while (true) {
1414                 MemberName element = getVolatile(tab, i);
1415                 if (element == null) {
1416                     return null;
1417                 }
1418                 if (element.equals(lookupElement)) {
1419                     return element;
1420                 }
1421                 i = nextKeyIndex(i, tab.length);
1422             }
1423         }
1424 
1425         /**
1426          * Inserts new element if there is no interned element equal to it
1427          * already present. This method must be called under exclusive lock.
1428          *
1429          * @param newElement new element to insert.
1430          * @return null if new element was inserted or old element if
1431          *         there was one (and no new element was inserted)
1432          */
1433         private MemberName putIfAbsent(MemberName newElement) {
1434             if (newElement == null) throw new NullPointerException();
1435 
1436             MemberName[] tab = table; // volatile read
1437 
1438             if (tab == null) {
1439                 // lazily create initial table
1440                 tab = new MemberName[MINIMUM_CAPACITY];
1441                 // still virgin
1442                 tab[hash(newElement, tab.length)] = newElement;
1443                 // publish initial table
1444                 table = tab;
1445                 size = 1;
1446                 return null;
1447             }
1448 
1449             while (true) {
1450 
1451                 int i = hash(newElement, tab.length);
1452 
1453                 for (MemberName element;
1454                      (element = tab[i]) != null;
1455                      i = nextKeyIndex(i, tab.length)) {
1456                     if (element.equals(newElement)) {
1457                         // return existing element
1458                         return element;
1459                     }
1460                 }
1461 
1462                 final int newSize = size + 1;
1463 
1464                 MemberName[] newTab;
1465                 if ((newTab = resize(tab, newSize)) != null) { // we needed to resize
1466                     // publish new table
1467                     table = tab = newTab;
1468                     // retry loop with new table
1469                 } else { // current tab.length is enough
1470                     // publish new element
1471                     setVolatile(tab, i, newElement);
1472                     size = newSize;
1473                     return null;
1474                 }

1475             }



1476         }
1477 
1478         // utility methods
1479 
1480         /**
1481          * Re-sizes the table if necessary to hold given number of elements.

1482          *
1483          * @return new table with elements copied to it if resizing was performed
1484          *         or null if no resizing is necessary because the table
1485          *         has enough room.
1486          */
1487         private static MemberName[] resize(MemberName[] oldTable, int newSize) {
1488             int oldLength = oldTable.length;
1489             int newLength = oldLength;
1490             // length should always be >= 3 * size / 2.
1491             while (newLength < newSize + (newSize >> 1) &&
1492                    newLength < MAXIMUM_CAPACITY) {
1493                 // next power of 2
1494                 newLength <<= 1;
1495             }
1496             if (oldLength == newLength) { // no resizing needed
1497                 if (newSize == MAXIMUM_CAPACITY)
1498                     throw new IllegalStateException("Capacity exhausted.");
1499                 // current length is enough
1500                 return null;
1501             }
1502 
1503             MemberName[] newTable = new MemberName[newLength];
1504 
1505             // copy elements to new table
1506             for (MemberName element : oldTable) {
1507                 if (element != null) {
1508                     int i = hash(element, newLength);
1509                     while (newTable[i] != null)
1510                         i = nextKeyIndex(i, newLength);
1511                     newTable[i] = element;
1512                 }
1513             }
1514 
1515             return newTable;
1516         }
1517 
1518         /**
1519          * Returns index for Object x.
1520          */
1521         private static int hash(Object x, int length) {
1522             int h = x.hashCode();
1523             return (h ^ (h >>> 16)) & (length - 1);
1524         }
1525 
1526         /**
1527          * Circularly traverses table of size length (which is a power of 2).
1528          */
1529         private static int nextKeyIndex(int i, int length) {
1530             return (i + 1) & (length - 1);
1531         }
1532 
1533         // Unsafe machinery
1534         private static final Unsafe UNSAFE = Unsafe.getUnsafe();
1535         private static final long MN_ARRAY_BASE;
1536         private static final int MN_ARRAY_INDEX_SHIFT;
1537 
1538         static {
1539             try {
1540                 MN_ARRAY_BASE = UNSAFE.arrayBaseOffset(
1541                     MemberName[].class
1542                 );
1543                 int scale = UNSAFE.arrayIndexScale(MemberName[].class);
1544                 if ((scale & (scale - 1)) != 0)
1545                     throw new Error("MemberName[] index scale not a power of two");
1546                 MN_ARRAY_INDEX_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
1547             } catch (Exception e) {
1548                 throw new Error(e);
1549             }
1550         }
1551 
1552         private static MemberName getVolatile(MemberName[] tab, int i) {
1553             return (MemberName) UNSAFE.getObjectVolatile(tab, arrayOffset(tab, i));
1554         }
1555 
1556         private static void setVolatile(MemberName[] tab, int i, MemberName element) {
1557             UNSAFE.putObjectVolatile(tab, arrayOffset(tab, i), element);
1558         }
1559 
1560         private static long arrayOffset(MemberName[] tab, int i) {
1561             if (i < 0 || i >= tab.length)
1562                 throw new ArrayIndexOutOfBoundsException(i);
1563             return ((long) i << MN_ARRAY_INDEX_SHIFT) + MN_ARRAY_BASE;
1564         }
1565     }
1566 }