< 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 "<init>"}.
+ * 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 "<init>"}.
*/
+ @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 "<init>"}.
- * 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 >