/* * 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 * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.lang.invoke; import sun.invoke.util.BytecodeDescriptor; import sun.invoke.util.VerifyAccess; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Member; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.*; import java.util.Objects; /** * A {@code MemberName} is a compact symbolic datum which fully characterizes * 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 *
* 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. *
* 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. *
* 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
*/
@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
//@Injected JVM_Method* vmtarget;
//@Injected int vmindex;
private Object resolution; // if null, this guy is resolved
// 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) {
MemberName tmp = new MemberName(m, wantSpecial);
// Unreflected member names are resolved so intern them here.
return tmp.intern();
}
/**
* Create a name for the given reflected constructor. The resulting name
* will be in a resolved state.
*/
@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() };
}
}
static public MemberName make(Constructor> ctor) {
MemberName tmp = new MemberName(ctor);
// Unreflected member names are resolved so intern them here.
return tmp.intern();
}
/** 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);
}
}
static public MemberName make(Field field, boolean makeSetter) {
MemberName tmp = new MemberName(field, makeSetter);
// Unreflected member names are resolved so intern them here.
return tmp.intern();
}
/** 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() {
return clazz.internMemberName(this);
}
/** Return the declaring class of this member.
* In the case of a bare name and type, the declaring class will be null.
*/
public Class> getDeclaringClass() {
return clazz;
}
/** Utility method producing the class loader of the declaring class. */
public ClassLoader getClassLoader() {
return clazz.getClassLoader();
}
/** 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>"}.
*/
public String getName() {
if (name == null) {
expandFromVM();
if (name == null) {
return null;
}
}
return name;
}
public MethodType getMethodOrFieldType() {
if (isInvocable())
return getMethodType();
if (isGetter())
return MethodType.methodType(getFieldType());
if (isSetter())
return MethodType.methodType(void.class, getFieldType());
throw new InternalError("not a method or field: "+this);
}
/** Return the declared type of this member, which
* must be a method or constructor.
*/
public MethodType getMethodType() {
if (type == null) {
expandFromVM();
if (type == null) {
return null;
}
}
if (!isInvocable()) {
throw newIllegalArgumentException("not invocable, no method type");
}
{
// Get a snapshot of type which doesn't get changed by racing threads.
final Object type = this.type;
if (type instanceof MethodType) {
return (MethodType) type;
}
}
// type is not a MethodType yet. Convert it thread-safely.
synchronized (this) {
if (type instanceof String) {
String sig = (String) type;
MethodType res = MethodType.fromMethodDescriptorString(sig, getClassLoader());
type = res;
} else if (type instanceof Object[]) {
Object[] typeInfo = (Object[]) type;
Class>[] ptypes = (Class>[]) typeInfo[1];
Class> rtype = (Class>) typeInfo[0];
MethodType res = MethodType.methodType(rtype, ptypes);
type = res;
}
// Make sure type is a MethodType for racing threads.
assert type instanceof MethodType : "bad method type " + type;
}
return (MethodType) type;
}
/** Return the actual type under which this method or constructor must be invoked.
* For non-static methods or constructors, this is the type with a leading parameter,
* a reference to declaring class. For static methods, it is the same as the declared type.
*/
public MethodType getInvocationType() {
MethodType itype = getMethodOrFieldType();
if (isConstructor() && getReferenceKind() == REF_newInvokeSpecial)
return itype.changeReturnType(clazz);
if (!isStatic())
return itype.insertParameterTypes(0, clazz);
return itype;
}
/** Utility method producing the parameter types of the method type. */
public Class>[] getParameterTypes() {
return getMethodType().parameterArray();
}
/** Utility method producing the return type of the method type. */
public Class> getReturnType() {
return getMethodType().returnType();
}
/** Return the declared type of this member, which
* must be a field or type.
* If it is a type member, that type itself is returned.
*/
public Class> getFieldType() {
if (type == null) {
expandFromVM();
if (type == null) {
return null;
}
}
if (isInvocable()) {
throw newIllegalArgumentException("not a field or nested class, no simple type");
}
{
// Get a snapshot of type which doesn't get changed by racing threads.
final Object type = this.type;
if (type instanceof Class>) {
return (Class>) type;
}
}
// type is not a Class yet. Convert it thread-safely.
synchronized (this) {
if (type instanceof String) {
String sig = (String) type;
MethodType mtype = MethodType.fromMethodDescriptorString("()"+sig, getClassLoader());
Class> res = mtype.returnType();
type = res;
}
// Make sure type is a Class for racing threads.
assert type instanceof Class> : "bad field type " + type;
}
return (Class>) type;
}
/** Utility method to produce either the method type or field type of this member. */
public Object getType() {
return (isInvocable() ? getMethodType() : getFieldType());
}
/** Utility method to produce the signature of this member,
* used within the class file format to describe its type.
*/
public String getSignature() {
if (type == null) {
expandFromVM();
if (type == null) {
return null;
}
}
if (isInvocable())
return BytecodeDescriptor.unparse(getMethodType());
else
return BytecodeDescriptor.unparse(getFieldType());
}
/** Return the modifier flags of this member.
* @see java.lang.reflect.Modifier
*/
public int getModifiers() {
return (flags & RECOGNIZED_MODIFIERS);
}
/** Return the reference kind of this member, or zero if none.
*/
public byte getReferenceKind() {
return (byte) ((flags >>> MN_REFERENCE_KIND_SHIFT) & MN_REFERENCE_KIND_MASK);
}
private boolean referenceKindIsConsistent() {
byte refKind = getReferenceKind();
if (refKind == REF_NONE) return isType();
if (isField()) {
assert(staticIsConsistent());
assert(MethodHandleNatives.refKindIsField(refKind));
} else if (isConstructor()) {
assert(refKind == REF_newInvokeSpecial || refKind == REF_invokeSpecial);
} else if (isMethod()) {
assert(staticIsConsistent());
assert(MethodHandleNatives.refKindIsMethod(refKind));
if (clazz.isInterface())
assert(refKind == REF_invokeInterface ||
refKind == REF_invokeStatic ||
refKind == REF_invokeSpecial ||
refKind == REF_invokeVirtual && isObjectPublicMethod());
} else {
assert(false);
}
return true;
}
private boolean isObjectPublicMethod() {
if (clazz == Object.class) return true;
MethodType mtype = getMethodType();
if (name.equals("toString") && mtype.returnType() == String.class && mtype.parameterCount() == 0)
return true;
if (name.equals("hashCode") && mtype.returnType() == int.class && mtype.parameterCount() == 0)
return true;
if (name.equals("equals") && mtype.returnType() == boolean.class && mtype.parameterCount() == 1 && mtype.parameterType(0) == Object.class)
return true;
return false;
}
/*non-public*/ boolean referenceKindIsConsistentWith(int originalRefKind) {
int refKind = getReferenceKind();
if (refKind == originalRefKind) return true;
switch (originalRefKind) {
case REF_invokeInterface:
// Looking up an interface method, can get (e.g.) Object.hashCode
assert(refKind == REF_invokeVirtual ||
refKind == REF_invokeSpecial) : this;
return true;
case REF_invokeVirtual:
case REF_newInvokeSpecial:
// Looked up a virtual, can get (e.g.) final String.hashCode.
assert(refKind == REF_invokeSpecial) : this;
return true;
}
assert(false) : this+" != "+MethodHandleNatives.refKindName((byte)originalRefKind);
return true;
}
private boolean staticIsConsistent() {
byte refKind = getReferenceKind();
return MethodHandleNatives.refKindIsStatic(refKind) == isStatic() || getModifiers() == 0;
}
private boolean vminfoIsConsistent() {
byte refKind = getReferenceKind();
assert(isResolved()); // else don't call
Object vminfo = MethodHandleNatives.getMemberVMInfo(this);
assert(vminfo instanceof Object[]);
long vmindex = (Long) ((Object[])vminfo)[0];
Object vmtarget = ((Object[])vminfo)[1];
if (MethodHandleNatives.refKindIsField(refKind)) {
assert(vmindex >= 0) : vmindex + ":" + this;
assert(vmtarget instanceof Class);
} else {
if (MethodHandleNatives.refKindDoesDispatch(refKind))
assert(vmindex >= 0) : vmindex + ":" + this;
else
assert(vmindex < 0) : vmindex;
assert(vmtarget instanceof MemberName) : vmtarget + " in " + this;
}
return true;
}
private MemberName changeReferenceKind(byte refKind, byte oldKind) {
assert(getReferenceKind() == oldKind);
assert(MethodHandleNatives.refKindIsValid(refKind));
flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT);
return this;
}
private boolean testFlags(int mask, int value) {
return (flags & mask) == value;
}
private boolean testAllFlags(int mask) {
return testFlags(mask, mask);
}
private boolean testAnyFlags(int mask) {
return !testFlags(mask, 0);
}
/** Utility method to query if this member is a method handle invocation (invoke or invokeExact).
* Also returns true for the non-public MH.invokeBasic.
*/
public boolean isMethodHandleInvoke() {
final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC;
final int negs = Modifier.STATIC;
if (testFlags(bits | negs, bits) &&
clazz == MethodHandle.class) {
return isMethodHandleInvokeName(name);
}
return false;
}
public static boolean isMethodHandleInvokeName(String name) {
switch (name) {
case "invoke":
case "invokeExact":
case "invokeBasic": // internal sig-poly method
return true;
default:
return false;
}
}
private static final int MH_INVOKE_MODS = Modifier.NATIVE | Modifier.FINAL | Modifier.PUBLIC;
/** Utility method to query the modifier flags of this member. */
public boolean isStatic() {
return Modifier.isStatic(flags);
}
/** Utility method to query the modifier flags of this member. */
public boolean isPublic() {
return Modifier.isPublic(flags);
}
/** Utility method to query the modifier flags of this member. */
public boolean isPrivate() {
return Modifier.isPrivate(flags);
}
/** Utility method to query the modifier flags of this member. */
public boolean isProtected() {
return Modifier.isProtected(flags);
}
/** Utility method to query the modifier flags of this member. */
public boolean isFinal() {
return Modifier.isFinal(flags);
}
/** Utility method to query whether this member or its defining class is final. */
public boolean canBeStaticallyBound() {
return Modifier.isFinal(flags | clazz.getModifiers());
}
/** Utility method to query the modifier flags of this member. */
public boolean isVolatile() {
return Modifier.isVolatile(flags);
}
/** Utility method to query the modifier flags of this member. */
public boolean isAbstract() {
return Modifier.isAbstract(flags);
}
/** Utility method to query the modifier flags of this member. */
public boolean isNative() {
return Modifier.isNative(flags);
}
// let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo
// unofficial modifier flags, used by HotSpot:
static final int BRIDGE = 0x00000040;
static final int VARARGS = 0x00000080;
static final int SYNTHETIC = 0x00001000;
static final int ANNOTATION= 0x00002000;
static final int ENUM = 0x00004000;
/** Utility method to query the modifier flags of this member; returns false if the member is not a method. */
public boolean isBridge() {
return testAllFlags(IS_METHOD | BRIDGE);
}
/** 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. */
public boolean isSynthetic() {
return testAllFlags(SYNTHETIC);
}
static final String CONSTRUCTOR_NAME = "