jaroslav@1646: /*
jaroslav@1646: * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
jaroslav@1646: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jaroslav@1646: *
jaroslav@1646: * This code is free software; you can redistribute it and/or modify it
jaroslav@1646: * under the terms of the GNU General Public License version 2 only, as
jaroslav@1646: * published by the Free Software Foundation. Oracle designates this
jaroslav@1646: * particular file as subject to the "Classpath" exception as provided
jaroslav@1646: * by Oracle in the LICENSE file that accompanied this code.
jaroslav@1646: *
jaroslav@1646: * This code is distributed in the hope that it will be useful, but WITHOUT
jaroslav@1646: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jaroslav@1646: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jaroslav@1646: * version 2 for more details (a copy is included in the LICENSE file that
jaroslav@1646: * accompanied this code).
jaroslav@1646: *
jaroslav@1646: * You should have received a copy of the GNU General Public License version
jaroslav@1646: * 2 along with this work; if not, write to the Free Software Foundation,
jaroslav@1646: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jaroslav@1646: *
jaroslav@1646: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jaroslav@1646: * or visit www.oracle.com if you need additional information or have any
jaroslav@1646: * questions.
jaroslav@1646: */
jaroslav@1646:
jaroslav@1646: package java.lang.invoke;
jaroslav@1646:
jaroslav@1646: import sun.invoke.util.BytecodeDescriptor;
jaroslav@1646: import sun.invoke.util.VerifyAccess;
jaroslav@1646:
jaroslav@1646: import java.lang.reflect.Constructor;
jaroslav@1646: import java.lang.reflect.Field;
jaroslav@1646: import java.lang.reflect.Method;
jaroslav@1646: import java.lang.reflect.Member;
jaroslav@1646: import java.lang.reflect.Modifier;
jaroslav@1646: import java.util.ArrayList;
jaroslav@1646: import java.util.Arrays;
jaroslav@1646: import java.util.Collections;
jaroslav@1646: import java.util.Iterator;
jaroslav@1646: import java.util.List;
jaroslav@1646: import static java.lang.invoke.MethodHandleNatives.Constants.*;
jaroslav@1646: import static java.lang.invoke.MethodHandleStatics.*;
jaroslav@1646: import java.util.Objects;
jaroslav@1646:
jaroslav@1646: /**
jaroslav@1646: * A {@code MemberName} is a compact symbolic datum which fully characterizes
jaroslav@1646: * a method or field reference.
jaroslav@1646: * A member name refers to a field, method, constructor, or member type.
jaroslav@1646: * Every member name has a simple name (a string) and a type (either a Class or MethodType).
jaroslav@1646: * A member name may also have a non-null declaring class, or it may be simply
jaroslav@1646: * a naked name/type pair.
jaroslav@1646: * A member name may also have non-zero modifier flags.
jaroslav@1646: * Finally, a member name may be either resolved or unresolved.
jaroslav@1646: * If it is resolved, the existence of the named
jaroslav@1646: *
jaroslav@1646: * Whether resolved or not, a member name provides no access rights or
jaroslav@1646: * invocation capability to its possessor. It is merely a compact
jaroslav@1646: * representation of all symbolic information necessary to link to
jaroslav@1646: * and properly use the named member.
jaroslav@1646: *
jaroslav@1646: * When resolved, a member name's internal implementation may include references to JVM metadata.
jaroslav@1646: * This representation is stateless and only decriptive.
jaroslav@1646: * It provides no private information and no capability to use the member.
jaroslav@1646: *
jaroslav@1646: * By contrast, a {@linkplain java.lang.reflect.Method} contains fuller information
jaroslav@1646: * about the internals of a method (except its bytecodes) and also
jaroslav@1646: * allows invocation. A MemberName is much lighter than a Method,
jaroslav@1646: * since it contains about 7 fields to the 16 of Method (plus its sub-arrays),
jaroslav@1646: * and those seven fields omit much of the information in Method.
jaroslav@1646: * @author jrose
jaroslav@1646: */
jaroslav@1646: /*non-public*/ final class MemberName implements Member, Cloneable {
jaroslav@1646: private Class> clazz; // class in which the method is defined
jaroslav@1646: private String name; // may be null if not yet materialized
jaroslav@1646: private Object type; // may be null if not yet materialized
jaroslav@1646: private int flags; // modifier bits; see reflect.Modifier
jaroslav@1646: //@Injected JVM_Method* vmtarget;
jaroslav@1646: //@Injected int vmindex;
jaroslav@1646: private Object resolution; // if null, this guy is resolved
jaroslav@1646:
jaroslav@1646: /** Return the declaring class of this member.
jaroslav@1646: * In the case of a bare name and type, the declaring class will be null.
jaroslav@1646: */
jaroslav@1646: public Class> getDeclaringClass() {
jaroslav@1646: return clazz;
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Utility method producing the class loader of the declaring class. */
jaroslav@1646: public ClassLoader getClassLoader() {
jaroslav@1646: return clazz.getClassLoader();
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Return the simple name of this member.
jaroslav@1646: * For a type, it is the same as {@link Class#getSimpleName}.
jaroslav@1646: * For a method or field, it is the simple name of the member.
jaroslav@1646: * For a constructor, it is always {@code "<init>"}.
jaroslav@1646: */
jaroslav@1646: public String getName() {
jaroslav@1646: if (name == null) {
jaroslav@1646: expandFromVM();
jaroslav@1646: if (name == null) {
jaroslav@1646: return null;
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646: return name;
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: public MethodType getMethodOrFieldType() {
jaroslav@1646: if (isInvocable())
jaroslav@1646: return getMethodType();
jaroslav@1646: if (isGetter())
jaroslav@1646: return MethodType.methodType(getFieldType());
jaroslav@1646: if (isSetter())
jaroslav@1646: return MethodType.methodType(void.class, getFieldType());
jaroslav@1646: throw new InternalError("not a method or field: "+this);
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Return the declared type of this member, which
jaroslav@1646: * must be a method or constructor.
jaroslav@1646: */
jaroslav@1646: public MethodType getMethodType() {
jaroslav@1646: if (type == null) {
jaroslav@1646: expandFromVM();
jaroslav@1646: if (type == null) {
jaroslav@1646: return null;
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646: if (!isInvocable()) {
jaroslav@1646: throw newIllegalArgumentException("not invocable, no method type");
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: {
jaroslav@1646: // Get a snapshot of type which doesn't get changed by racing threads.
jaroslav@1646: final Object type = this.type;
jaroslav@1646: if (type instanceof MethodType) {
jaroslav@1646: return (MethodType) type;
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: // type is not a MethodType yet. Convert it thread-safely.
jaroslav@1646: synchronized (this) {
jaroslav@1646: if (type instanceof String) {
jaroslav@1646: String sig = (String) type;
jaroslav@1646: MethodType res = MethodType.fromMethodDescriptorString(sig, getClassLoader());
jaroslav@1646: type = res;
jaroslav@1646: } else if (type instanceof Object[]) {
jaroslav@1646: Object[] typeInfo = (Object[]) type;
jaroslav@1646: Class>[] ptypes = (Class>[]) typeInfo[1];
jaroslav@1646: Class> rtype = (Class>) typeInfo[0];
jaroslav@1646: MethodType res = MethodType.methodType(rtype, ptypes);
jaroslav@1646: type = res;
jaroslav@1646: }
jaroslav@1646: // Make sure type is a MethodType for racing threads.
jaroslav@1646: assert type instanceof MethodType : "bad method type " + type;
jaroslav@1646: }
jaroslav@1646: return (MethodType) type;
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Return the actual type under which this method or constructor must be invoked.
jaroslav@1646: * For non-static methods or constructors, this is the type with a leading parameter,
jaroslav@1646: * a reference to declaring class. For static methods, it is the same as the declared type.
jaroslav@1646: */
jaroslav@1646: public MethodType getInvocationType() {
jaroslav@1646: MethodType itype = getMethodOrFieldType();
jaroslav@1646: if (isConstructor() && getReferenceKind() == REF_newInvokeSpecial)
jaroslav@1646: return itype.changeReturnType(clazz);
jaroslav@1646: if (!isStatic())
jaroslav@1646: return itype.insertParameterTypes(0, clazz);
jaroslav@1646: return itype;
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Utility method producing the parameter types of the method type. */
jaroslav@1646: public Class>[] getParameterTypes() {
jaroslav@1646: return getMethodType().parameterArray();
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Utility method producing the return type of the method type. */
jaroslav@1646: public Class> getReturnType() {
jaroslav@1646: return getMethodType().returnType();
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Return the declared type of this member, which
jaroslav@1646: * must be a field or type.
jaroslav@1646: * If it is a type member, that type itself is returned.
jaroslav@1646: */
jaroslav@1646: public Class> getFieldType() {
jaroslav@1646: if (type == null) {
jaroslav@1646: expandFromVM();
jaroslav@1646: if (type == null) {
jaroslav@1646: return null;
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646: if (isInvocable()) {
jaroslav@1646: throw newIllegalArgumentException("not a field or nested class, no simple type");
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: {
jaroslav@1646: // Get a snapshot of type which doesn't get changed by racing threads.
jaroslav@1646: final Object type = this.type;
jaroslav@1646: if (type instanceof Class>) {
jaroslav@1646: return (Class>) type;
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: // type is not a Class yet. Convert it thread-safely.
jaroslav@1646: synchronized (this) {
jaroslav@1646: if (type instanceof String) {
jaroslav@1646: String sig = (String) type;
jaroslav@1646: MethodType mtype = MethodType.fromMethodDescriptorString("()"+sig, getClassLoader());
jaroslav@1646: Class> res = mtype.returnType();
jaroslav@1646: type = res;
jaroslav@1646: }
jaroslav@1646: // Make sure type is a Class for racing threads.
jaroslav@1646: assert type instanceof Class> : "bad field type " + type;
jaroslav@1646: }
jaroslav@1646: return (Class>) type;
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Utility method to produce either the method type or field type of this member. */
jaroslav@1646: public Object getType() {
jaroslav@1646: return (isInvocable() ? getMethodType() : getFieldType());
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Utility method to produce the signature of this member,
jaroslav@1646: * used within the class file format to describe its type.
jaroslav@1646: */
jaroslav@1646: public String getSignature() {
jaroslav@1646: if (type == null) {
jaroslav@1646: expandFromVM();
jaroslav@1646: if (type == null) {
jaroslav@1646: return null;
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646: if (isInvocable())
jaroslav@1646: return BytecodeDescriptor.unparse(getMethodType());
jaroslav@1646: else
jaroslav@1646: return BytecodeDescriptor.unparse(getFieldType());
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Return the modifier flags of this member.
jaroslav@1646: * @see java.lang.reflect.Modifier
jaroslav@1646: */
jaroslav@1646: public int getModifiers() {
jaroslav@1646: return (flags & RECOGNIZED_MODIFIERS);
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Return the reference kind of this member, or zero if none.
jaroslav@1646: */
jaroslav@1646: public byte getReferenceKind() {
jaroslav@1646: return (byte) ((flags >>> MN_REFERENCE_KIND_SHIFT) & MN_REFERENCE_KIND_MASK);
jaroslav@1646: }
jaroslav@1646: private boolean referenceKindIsConsistent() {
jaroslav@1646: byte refKind = getReferenceKind();
jaroslav@1646: if (refKind == REF_NONE) return isType();
jaroslav@1646: if (isField()) {
jaroslav@1646: assert(staticIsConsistent());
jaroslav@1646: assert(MethodHandleNatives.refKindIsField(refKind));
jaroslav@1646: } else if (isConstructor()) {
jaroslav@1646: assert(refKind == REF_newInvokeSpecial || refKind == REF_invokeSpecial);
jaroslav@1646: } else if (isMethod()) {
jaroslav@1646: assert(staticIsConsistent());
jaroslav@1646: assert(MethodHandleNatives.refKindIsMethod(refKind));
jaroslav@1646: if (clazz.isInterface())
jaroslav@1646: assert(refKind == REF_invokeInterface ||
jaroslav@1646: refKind == REF_invokeStatic ||
jaroslav@1646: refKind == REF_invokeSpecial ||
jaroslav@1646: refKind == REF_invokeVirtual && isObjectPublicMethod());
jaroslav@1646: } else {
jaroslav@1646: assert(false);
jaroslav@1646: }
jaroslav@1646: return true;
jaroslav@1646: }
jaroslav@1646: private boolean isObjectPublicMethod() {
jaroslav@1646: if (clazz == Object.class) return true;
jaroslav@1646: MethodType mtype = getMethodType();
jaroslav@1646: if (name.equals("toString") && mtype.returnType() == String.class && mtype.parameterCount() == 0)
jaroslav@1646: return true;
jaroslav@1646: if (name.equals("hashCode") && mtype.returnType() == int.class && mtype.parameterCount() == 0)
jaroslav@1646: return true;
jaroslav@1646: if (name.equals("equals") && mtype.returnType() == boolean.class && mtype.parameterCount() == 1 && mtype.parameterType(0) == Object.class)
jaroslav@1646: return true;
jaroslav@1646: return false;
jaroslav@1646: }
jaroslav@1646: /*non-public*/ boolean referenceKindIsConsistentWith(int originalRefKind) {
jaroslav@1646: int refKind = getReferenceKind();
jaroslav@1646: if (refKind == originalRefKind) return true;
jaroslav@1646: switch (originalRefKind) {
jaroslav@1646: case REF_invokeInterface:
jaroslav@1646: // Looking up an interface method, can get (e.g.) Object.hashCode
jaroslav@1646: assert(refKind == REF_invokeVirtual ||
jaroslav@1646: refKind == REF_invokeSpecial) : this;
jaroslav@1646: return true;
jaroslav@1646: case REF_invokeVirtual:
jaroslav@1646: case REF_newInvokeSpecial:
jaroslav@1646: // Looked up a virtual, can get (e.g.) final String.hashCode.
jaroslav@1646: assert(refKind == REF_invokeSpecial) : this;
jaroslav@1646: return true;
jaroslav@1646: }
jaroslav@1646: assert(false) : this+" != "+MethodHandleNatives.refKindName((byte)originalRefKind);
jaroslav@1646: return true;
jaroslav@1646: }
jaroslav@1646: private boolean staticIsConsistent() {
jaroslav@1646: byte refKind = getReferenceKind();
jaroslav@1646: return MethodHandleNatives.refKindIsStatic(refKind) == isStatic() || getModifiers() == 0;
jaroslav@1646: }
jaroslav@1646: private boolean vminfoIsConsistent() {
jaroslav@1646: byte refKind = getReferenceKind();
jaroslav@1646: assert(isResolved()); // else don't call
jaroslav@1646: Object vminfo = MethodHandleNatives.getMemberVMInfo(this);
jaroslav@1646: assert(vminfo instanceof Object[]);
jaroslav@1646: long vmindex = (Long) ((Object[])vminfo)[0];
jaroslav@1646: Object vmtarget = ((Object[])vminfo)[1];
jaroslav@1646: if (MethodHandleNatives.refKindIsField(refKind)) {
jaroslav@1646: assert(vmindex >= 0) : vmindex + ":" + this;
jaroslav@1646: assert(vmtarget instanceof Class);
jaroslav@1646: } else {
jaroslav@1646: if (MethodHandleNatives.refKindDoesDispatch(refKind))
jaroslav@1646: assert(vmindex >= 0) : vmindex + ":" + this;
jaroslav@1646: else
jaroslav@1646: assert(vmindex < 0) : vmindex;
jaroslav@1646: assert(vmtarget instanceof MemberName) : vmtarget + " in " + this;
jaroslav@1646: }
jaroslav@1646: return true;
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: private MemberName changeReferenceKind(byte refKind, byte oldKind) {
jaroslav@1646: assert(getReferenceKind() == oldKind);
jaroslav@1646: assert(MethodHandleNatives.refKindIsValid(refKind));
jaroslav@1646: flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT);
jaroslav@1646: // if (isConstructor() && refKind != REF_newInvokeSpecial)
jaroslav@1646: // flags += (IS_METHOD - IS_CONSTRUCTOR);
jaroslav@1646: // else if (refKind == REF_newInvokeSpecial && isMethod())
jaroslav@1646: // flags += (IS_CONSTRUCTOR - IS_METHOD);
jaroslav@1646: return this;
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: private boolean testFlags(int mask, int value) {
jaroslav@1646: return (flags & mask) == value;
jaroslav@1646: }
jaroslav@1646: private boolean testAllFlags(int mask) {
jaroslav@1646: return testFlags(mask, mask);
jaroslav@1646: }
jaroslav@1646: private boolean testAnyFlags(int mask) {
jaroslav@1646: return !testFlags(mask, 0);
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */
jaroslav@1646: public boolean isMethodHandleInvoke() {
jaroslav@1646: final int bits = MH_INVOKE_MODS;
jaroslav@1646: final int negs = Modifier.STATIC;
jaroslav@1646: if (testFlags(bits | negs, bits) &&
jaroslav@1646: clazz == MethodHandle.class) {
jaroslav@1646: return isMethodHandleInvokeName(name);
jaroslav@1646: }
jaroslav@1646: return false;
jaroslav@1646: }
jaroslav@1646: public static boolean isMethodHandleInvokeName(String name) {
jaroslav@1646: return name.equals("invoke") || name.equals("invokeExact");
jaroslav@1646: }
jaroslav@1646: private static final int MH_INVOKE_MODS = Modifier.NATIVE | Modifier.FINAL | Modifier.PUBLIC;
jaroslav@1646:
jaroslav@1646: /** Utility method to query the modifier flags of this member. */
jaroslav@1646: public boolean isStatic() {
jaroslav@1646: return Modifier.isStatic(flags);
jaroslav@1646: }
jaroslav@1646: /** Utility method to query the modifier flags of this member. */
jaroslav@1646: public boolean isPublic() {
jaroslav@1646: return Modifier.isPublic(flags);
jaroslav@1646: }
jaroslav@1646: /** Utility method to query the modifier flags of this member. */
jaroslav@1646: public boolean isPrivate() {
jaroslav@1646: return Modifier.isPrivate(flags);
jaroslav@1646: }
jaroslav@1646: /** Utility method to query the modifier flags of this member. */
jaroslav@1646: public boolean isProtected() {
jaroslav@1646: return Modifier.isProtected(flags);
jaroslav@1646: }
jaroslav@1646: /** Utility method to query the modifier flags of this member. */
jaroslav@1646: public boolean isFinal() {
jaroslav@1646: return Modifier.isFinal(flags);
jaroslav@1646: }
jaroslav@1646: /** Utility method to query whether this member or its defining class is final. */
jaroslav@1646: public boolean canBeStaticallyBound() {
jaroslav@1646: return Modifier.isFinal(flags | clazz.getModifiers());
jaroslav@1646: }
jaroslav@1646: /** Utility method to query the modifier flags of this member. */
jaroslav@1646: public boolean isVolatile() {
jaroslav@1646: return Modifier.isVolatile(flags);
jaroslav@1646: }
jaroslav@1646: /** Utility method to query the modifier flags of this member. */
jaroslav@1646: public boolean isAbstract() {
jaroslav@1646: return Modifier.isAbstract(flags);
jaroslav@1646: }
jaroslav@1646: /** Utility method to query the modifier flags of this member. */
jaroslav@1646: public boolean isNative() {
jaroslav@1646: return Modifier.isNative(flags);
jaroslav@1646: }
jaroslav@1646: // let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo
jaroslav@1646:
jaroslav@1646: // unofficial modifier flags, used by HotSpot:
jaroslav@1646: static final int BRIDGE = 0x00000040;
jaroslav@1646: static final int VARARGS = 0x00000080;
jaroslav@1646: static final int SYNTHETIC = 0x00001000;
jaroslav@1646: static final int ANNOTATION= 0x00002000;
jaroslav@1646: static final int ENUM = 0x00004000;
jaroslav@1646: /** Utility method to query the modifier flags of this member; returns false if the member is not a method. */
jaroslav@1646: public boolean isBridge() {
jaroslav@1646: return testAllFlags(IS_METHOD | BRIDGE);
jaroslav@1646: }
jaroslav@1646: /** Utility method to query the modifier flags of this member; returns false if the member is not a method. */
jaroslav@1646: public boolean isVarargs() {
jaroslav@1646: return testAllFlags(VARARGS) && isInvocable();
jaroslav@1646: }
jaroslav@1646: /** Utility method to query the modifier flags of this member; returns false if the member is not a method. */
jaroslav@1646: public boolean isSynthetic() {
jaroslav@1646: return testAllFlags(SYNTHETIC);
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: static final String CONSTRUCTOR_NAME = ""; // the ever-popular
jaroslav@1646:
jaroslav@1646: // modifiers exported by the JVM:
jaroslav@1646: static final int RECOGNIZED_MODIFIERS = 0xFFFF;
jaroslav@1646:
jaroslav@1646: // private flags, not part of RECOGNIZED_MODIFIERS:
jaroslav@1646: static final int
jaroslav@1646: IS_METHOD = MN_IS_METHOD, // method (not constructor)
jaroslav@1646: IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor
jaroslav@1646: IS_FIELD = MN_IS_FIELD, // field
jaroslav@1646: IS_TYPE = MN_IS_TYPE, // nested type
jaroslav@1646: CALLER_SENSITIVE = MN_CALLER_SENSITIVE; // @CallerSensitive annotation detected
jaroslav@1646:
jaroslav@1646: static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED;
jaroslav@1646: static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE;
jaroslav@1646: static final int IS_INVOCABLE = IS_METHOD | IS_CONSTRUCTOR;
jaroslav@1646: static final int IS_FIELD_OR_METHOD = IS_METHOD | IS_FIELD;
jaroslav@1646: static final int SEARCH_ALL_SUPERS = MN_SEARCH_SUPERCLASSES | MN_SEARCH_INTERFACES;
jaroslav@1646:
jaroslav@1646: /** Utility method to query whether this member is a method or constructor. */
jaroslav@1646: public boolean isInvocable() {
jaroslav@1646: return testAnyFlags(IS_INVOCABLE);
jaroslav@1646: }
jaroslav@1646: /** Utility method to query whether this member is a method, constructor, or field. */
jaroslav@1646: public boolean isFieldOrMethod() {
jaroslav@1646: return testAnyFlags(IS_FIELD_OR_METHOD);
jaroslav@1646: }
jaroslav@1646: /** Query whether this member is a method. */
jaroslav@1646: public boolean isMethod() {
jaroslav@1646: return testAllFlags(IS_METHOD);
jaroslav@1646: }
jaroslav@1646: /** Query whether this member is a constructor. */
jaroslav@1646: public boolean isConstructor() {
jaroslav@1646: return testAllFlags(IS_CONSTRUCTOR);
jaroslav@1646: }
jaroslav@1646: /** Query whether this member is a field. */
jaroslav@1646: public boolean isField() {
jaroslav@1646: return testAllFlags(IS_FIELD);
jaroslav@1646: }
jaroslav@1646: /** Query whether this member is a type. */
jaroslav@1646: public boolean isType() {
jaroslav@1646: return testAllFlags(IS_TYPE);
jaroslav@1646: }
jaroslav@1646: /** Utility method to query whether this member is neither public, private, nor protected. */
jaroslav@1646: public boolean isPackage() {
jaroslav@1646: return !testAnyFlags(ALL_ACCESS);
jaroslav@1646: }
jaroslav@1646: /** Query whether this member has a CallerSensitive annotation. */
jaroslav@1646: public boolean isCallerSensitive() {
jaroslav@1646: return testAllFlags(CALLER_SENSITIVE);
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Utility method to query whether this member is accessible from a given lookup class. */
jaroslav@1646: public boolean isAccessibleFrom(Class> lookupClass) {
jaroslav@1646: return VerifyAccess.isMemberAccessible(this.getDeclaringClass(), this.getDeclaringClass(), flags,
jaroslav@1646: lookupClass, ALL_ACCESS|MethodHandles.Lookup.PACKAGE);
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Initialize a query. It is not resolved. */
jaroslav@1646: private void init(Class> defClass, String name, Object type, int flags) {
jaroslav@1646: // defining class is allowed to be null (for a naked name/type pair)
jaroslav@1646: //name.toString(); // null check
jaroslav@1646: //type.equals(type); // null check
jaroslav@1646: // fill in fields:
jaroslav@1646: this.clazz = defClass;
jaroslav@1646: this.name = name;
jaroslav@1646: this.type = type;
jaroslav@1646: this.flags = flags;
jaroslav@1646: assert(testAnyFlags(ALL_KINDS));
jaroslav@1646: assert(this.resolution == null); // nobody should have touched this yet
jaroslav@1646: //assert(referenceKindIsConsistent()); // do this after resolution
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /**
jaroslav@1646: * Calls down to the VM to fill in the fields. This method is
jaroslav@1646: * synchronized to avoid racing calls.
jaroslav@1646: */
jaroslav@1646: private void expandFromVM() {
jaroslav@1646: if (type != null) {
jaroslav@1646: return;
jaroslav@1646: }
jaroslav@1646: if (!isResolved()) {
jaroslav@1646: return;
jaroslav@1646: }
jaroslav@1646: MethodHandleNatives.expand(this);
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: // Capturing information from the Core Reflection API:
jaroslav@1646: private static int flagsMods(int flags, int mods, byte refKind) {
jaroslav@1646: assert((flags & RECOGNIZED_MODIFIERS) == 0);
jaroslav@1646: assert((mods & ~RECOGNIZED_MODIFIERS) == 0);
jaroslav@1646: assert((refKind & ~MN_REFERENCE_KIND_MASK) == 0);
jaroslav@1646: return flags | mods | (refKind << MN_REFERENCE_KIND_SHIFT);
jaroslav@1646: }
jaroslav@1646: /** Create a name for the given reflected method. The resulting name will be in a resolved state. */
jaroslav@1646: public MemberName(Method m) {
jaroslav@1646: this(m, false);
jaroslav@1646: }
jaroslav@1646: @SuppressWarnings("LeakingThisInConstructor")
jaroslav@1646: public MemberName(Method m, boolean wantSpecial) {
jaroslav@1646: m.getClass(); // NPE check
jaroslav@1646: // fill in vmtarget, vmindex while we have m in hand:
jaroslav@1646: MethodHandleNatives.init(this, m);
jaroslav@1646: if (clazz == null) { // MHN.init failed
jaroslav@1646: if (m.getDeclaringClass() == MethodHandle.class &&
jaroslav@1646: isMethodHandleInvokeName(m.getName())) {
jaroslav@1646: // The JVM did not reify this signature-polymorphic instance.
jaroslav@1646: // Need a special case here.
jaroslav@1646: // See comments on MethodHandleNatives.linkMethod.
jaroslav@1646: MethodType type = MethodType.methodType(m.getReturnType(), m.getParameterTypes());
jaroslav@1646: int flags = flagsMods(IS_METHOD, m.getModifiers(), REF_invokeVirtual);
jaroslav@1646: init(MethodHandle.class, m.getName(), type, flags);
jaroslav@1646: if (isMethodHandleInvoke())
jaroslav@1646: return;
jaroslav@1646: }
jaroslav@1646: throw new LinkageError(m.toString());
jaroslav@1646: }
jaroslav@1646: assert(isResolved() && this.clazz != null);
jaroslav@1646: this.name = m.getName();
jaroslav@1646: if (this.type == null)
jaroslav@1646: this.type = new Object[] { m.getReturnType(), m.getParameterTypes() };
jaroslav@1646: if (wantSpecial) {
jaroslav@1646: if (isAbstract())
jaroslav@1646: throw new AbstractMethodError(this.toString());
jaroslav@1646: if (getReferenceKind() == REF_invokeVirtual)
jaroslav@1646: changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
jaroslav@1646: else if (getReferenceKind() == REF_invokeInterface)
jaroslav@1646: // invokeSpecial on a default method
jaroslav@1646: changeReferenceKind(REF_invokeSpecial, REF_invokeInterface);
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646: public MemberName asSpecial() {
jaroslav@1646: switch (getReferenceKind()) {
jaroslav@1646: case REF_invokeSpecial: return this;
jaroslav@1646: case REF_invokeVirtual: return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
jaroslav@1646: case REF_invokeInterface: return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeInterface);
jaroslav@1646: case REF_newInvokeSpecial: return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial);
jaroslav@1646: }
jaroslav@1646: throw new IllegalArgumentException(this.toString());
jaroslav@1646: }
jaroslav@1646: /** If this MN is not REF_newInvokeSpecial, return a clone with that ref. kind.
jaroslav@1646: * In that case it must already be REF_invokeSpecial.
jaroslav@1646: */
jaroslav@1646: public MemberName asConstructor() {
jaroslav@1646: switch (getReferenceKind()) {
jaroslav@1646: case REF_invokeSpecial: return clone().changeReferenceKind(REF_newInvokeSpecial, REF_invokeSpecial);
jaroslav@1646: case REF_newInvokeSpecial: return this;
jaroslav@1646: }
jaroslav@1646: throw new IllegalArgumentException(this.toString());
jaroslav@1646: }
jaroslav@1646: /** If this MN is a REF_invokeSpecial, return a clone with the "normal" kind
jaroslav@1646: * REF_invokeVirtual; also switch either to REF_invokeInterface if clazz.isInterface.
jaroslav@1646: * The end result is to get a fully virtualized version of the MN.
jaroslav@1646: * (Note that resolving in the JVM will sometimes devirtualize, changing
jaroslav@1646: * REF_invokeVirtual of a final to REF_invokeSpecial, and REF_invokeInterface
jaroslav@1646: * in some corner cases to either of the previous two; this transform
jaroslav@1646: * undoes that change under the assumption that it occurred.)
jaroslav@1646: */
jaroslav@1646: public MemberName asNormalOriginal() {
jaroslav@1646: byte normalVirtual = clazz.isInterface() ? REF_invokeInterface : REF_invokeVirtual;
jaroslav@1646: byte refKind = getReferenceKind();
jaroslav@1646: byte newRefKind = refKind;
jaroslav@1646: MemberName result = this;
jaroslav@1646: switch (refKind) {
jaroslav@1646: case REF_invokeInterface:
jaroslav@1646: case REF_invokeVirtual:
jaroslav@1646: case REF_invokeSpecial:
jaroslav@1646: newRefKind = normalVirtual;
jaroslav@1646: break;
jaroslav@1646: }
jaroslav@1646: if (newRefKind == refKind)
jaroslav@1646: return this;
jaroslav@1646: result = clone().changeReferenceKind(newRefKind, refKind);
jaroslav@1646: assert(this.referenceKindIsConsistentWith(result.getReferenceKind()));
jaroslav@1646: return result;
jaroslav@1646: }
jaroslav@1646: /** Create a name for the given reflected constructor. The resulting name will be in a resolved state. */
jaroslav@1646: @SuppressWarnings("LeakingThisInConstructor")
jaroslav@1646: public MemberName(Constructor> ctor) {
jaroslav@1646: ctor.getClass(); // NPE check
jaroslav@1646: // fill in vmtarget, vmindex while we have ctor in hand:
jaroslav@1646: MethodHandleNatives.init(this, ctor);
jaroslav@1646: assert(isResolved() && this.clazz != null);
jaroslav@1646: this.name = CONSTRUCTOR_NAME;
jaroslav@1646: if (this.type == null)
jaroslav@1646: this.type = new Object[] { void.class, ctor.getParameterTypes() };
jaroslav@1646: }
jaroslav@1646: /** Create a name for the given reflected field. The resulting name will be in a resolved state.
jaroslav@1646: */
jaroslav@1646: public MemberName(Field fld) {
jaroslav@1646: this(fld, false);
jaroslav@1646: }
jaroslav@1646: @SuppressWarnings("LeakingThisInConstructor")
jaroslav@1646: public MemberName(Field fld, boolean makeSetter) {
jaroslav@1646: fld.getClass(); // NPE check
jaroslav@1646: // fill in vmtarget, vmindex while we have fld in hand:
jaroslav@1646: MethodHandleNatives.init(this, fld);
jaroslav@1646: assert(isResolved() && this.clazz != null);
jaroslav@1646: this.name = fld.getName();
jaroslav@1646: this.type = fld.getType();
jaroslav@1646: assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField));
jaroslav@1646: byte refKind = this.getReferenceKind();
jaroslav@1646: assert(refKind == (isStatic() ? REF_getStatic : REF_getField));
jaroslav@1646: if (makeSetter) {
jaroslav@1646: changeReferenceKind((byte)(refKind + (REF_putStatic - REF_getStatic)), refKind);
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646: public boolean isGetter() {
jaroslav@1646: return MethodHandleNatives.refKindIsGetter(getReferenceKind());
jaroslav@1646: }
jaroslav@1646: public boolean isSetter() {
jaroslav@1646: return MethodHandleNatives.refKindIsSetter(getReferenceKind());
jaroslav@1646: }
jaroslav@1646: public MemberName asSetter() {
jaroslav@1646: byte refKind = getReferenceKind();
jaroslav@1646: assert(MethodHandleNatives.refKindIsGetter(refKind));
jaroslav@1646: assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField));
jaroslav@1646: byte setterRefKind = (byte)(refKind + (REF_putField - REF_getField));
jaroslav@1646: return clone().changeReferenceKind(setterRefKind, refKind);
jaroslav@1646: }
jaroslav@1646: /** Create a name for the given class. The resulting name will be in a resolved state. */
jaroslav@1646: public MemberName(Class> type) {
jaroslav@1646: init(type.getDeclaringClass(), type.getSimpleName(), type,
jaroslav@1646: flagsMods(IS_TYPE, type.getModifiers(), REF_NONE));
jaroslav@1646: initResolved(true);
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /**
jaroslav@1646: * Create a name for a signature-polymorphic invoker.
jaroslav@1646: * This is a placeholder for a signature-polymorphic instance
jaroslav@1646: * (of MH.invokeExact, etc.) that the JVM does not reify.
jaroslav@1646: * See comments on {@link MethodHandleNatives#linkMethod}.
jaroslav@1646: */
jaroslav@1646: static MemberName makeMethodHandleInvoke(String name, MethodType type) {
jaroslav@1646: return makeMethodHandleInvoke(name, type, MH_INVOKE_MODS | SYNTHETIC);
jaroslav@1646: }
jaroslav@1646: static MemberName makeMethodHandleInvoke(String name, MethodType type, int mods) {
jaroslav@1646: MemberName mem = new MemberName(MethodHandle.class, name, type, REF_invokeVirtual);
jaroslav@1646: mem.flags |= mods; // it's not resolved, but add these modifiers anyway
jaroslav@1646: assert(mem.isMethodHandleInvoke()) : mem;
jaroslav@1646: return mem;
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: // bare-bones constructor; the JVM will fill it in
jaroslav@1646: MemberName() { }
jaroslav@1646:
jaroslav@1646: // locally useful cloner
jaroslav@1646: @Override protected MemberName clone() {
jaroslav@1646: try {
jaroslav@1646: return (MemberName) super.clone();
jaroslav@1646: } catch (CloneNotSupportedException ex) {
jaroslav@1646: throw newInternalError(ex);
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Get the definition of this member name.
jaroslav@1646: * This may be in a super-class of the declaring class of this member.
jaroslav@1646: */
jaroslav@1646: public MemberName getDefinition() {
jaroslav@1646: if (!isResolved()) throw new IllegalStateException("must be resolved: "+this);
jaroslav@1646: if (isType()) return this;
jaroslav@1646: MemberName res = this.clone();
jaroslav@1646: res.clazz = null;
jaroslav@1646: res.type = null;
jaroslav@1646: res.name = null;
jaroslav@1646: res.resolution = res;
jaroslav@1646: res.expandFromVM();
jaroslav@1646: assert(res.getName().equals(this.getName()));
jaroslav@1646: return res;
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: @Override
jaroslav@1646: public int hashCode() {
jaroslav@1646: return Objects.hash(clazz, getReferenceKind(), name, getType());
jaroslav@1646: }
jaroslav@1646: @Override
jaroslav@1646: public boolean equals(Object that) {
jaroslav@1646: return (that instanceof MemberName && this.equals((MemberName)that));
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Decide if two member names have exactly the same symbolic content.
jaroslav@1646: * Does not take into account any actual class members, so even if
jaroslav@1646: * two member names resolve to the same actual member, they may
jaroslav@1646: * be distinct references.
jaroslav@1646: */
jaroslav@1646: public boolean equals(MemberName that) {
jaroslav@1646: if (this == that) return true;
jaroslav@1646: if (that == null) return false;
jaroslav@1646: return this.clazz == that.clazz
jaroslav@1646: && this.getReferenceKind() == that.getReferenceKind()
jaroslav@1646: && Objects.equals(this.name, that.name)
jaroslav@1646: && Objects.equals(this.getType(), that.getType());
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: // Construction from symbolic parts, for queries:
jaroslav@1646: /** Create a field or type name from the given components:
jaroslav@1646: * Declaring class, name, type, reference kind.
jaroslav@1646: * The declaring class may be supplied as null if this is to be a bare name and type.
jaroslav@1646: * The resulting name will in an unresolved state.
jaroslav@1646: */
jaroslav@1646: public MemberName(Class> defClass, String name, Class> type, byte refKind) {
jaroslav@1646: init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind));
jaroslav@1646: initResolved(false);
jaroslav@1646: }
jaroslav@1646: /** Create a field or type name from the given components: Declaring class, name, type.
jaroslav@1646: * The declaring class may be supplied as null if this is to be a bare name and type.
jaroslav@1646: * The modifier flags default to zero.
jaroslav@1646: * The resulting name will in an unresolved state.
jaroslav@1646: */
jaroslav@1646: public MemberName(Class> defClass, String name, Class> type, Void unused) {
jaroslav@1646: this(defClass, name, type, REF_NONE);
jaroslav@1646: initResolved(false);
jaroslav@1646: }
jaroslav@1646: /** Create a method or constructor name from the given components: Declaring class, name, type, modifiers.
jaroslav@1646: * It will be a constructor if and only if the name is {@code "<init>"}.
jaroslav@1646: * The declaring class may be supplied as null if this is to be a bare name and type.
jaroslav@1646: * The last argument is optional, a boolean which requests REF_invokeSpecial.
jaroslav@1646: * The resulting name will in an unresolved state.
jaroslav@1646: */
jaroslav@1646: public MemberName(Class> defClass, String name, MethodType type, byte refKind) {
jaroslav@1646: int initFlags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
jaroslav@1646: init(defClass, name, type, flagsMods(initFlags, 0, refKind));
jaroslav@1646: initResolved(false);
jaroslav@1646: }
jaroslav@1646: /** Create a method, constructor, or field name from the given components:
jaroslav@1646: * Reference kind, declaring class, name, type.
jaroslav@1646: */
jaroslav@1646: public MemberName(byte refKind, Class> defClass, String name, Object type) {
jaroslav@1646: int kindFlags;
jaroslav@1646: if (MethodHandleNatives.refKindIsField(refKind)) {
jaroslav@1646: kindFlags = IS_FIELD;
jaroslav@1646: if (!(type instanceof Class))
jaroslav@1646: throw newIllegalArgumentException("not a field type");
jaroslav@1646: } else if (MethodHandleNatives.refKindIsMethod(refKind)) {
jaroslav@1646: kindFlags = IS_METHOD;
jaroslav@1646: if (!(type instanceof MethodType))
jaroslav@1646: throw newIllegalArgumentException("not a method type");
jaroslav@1646: } else if (refKind == REF_newInvokeSpecial) {
jaroslav@1646: kindFlags = IS_CONSTRUCTOR;
jaroslav@1646: if (!(type instanceof MethodType) ||
jaroslav@1646: !CONSTRUCTOR_NAME.equals(name))
jaroslav@1646: throw newIllegalArgumentException("not a constructor type or name");
jaroslav@1646: } else {
jaroslav@1646: throw newIllegalArgumentException("bad reference kind "+refKind);
jaroslav@1646: }
jaroslav@1646: init(defClass, name, type, flagsMods(kindFlags, 0, refKind));
jaroslav@1646: initResolved(false);
jaroslav@1646: }
jaroslav@1646: /** Query whether this member name is resolved to a non-static, non-final method.
jaroslav@1646: */
jaroslav@1646: public boolean hasReceiverTypeDispatch() {
jaroslav@1646: return MethodHandleNatives.refKindDoesDispatch(getReferenceKind());
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Query whether this member name is resolved.
jaroslav@1646: * A resolved member name is one for which the JVM has found
jaroslav@1646: * a method, constructor, field, or type binding corresponding exactly to the name.
jaroslav@1646: * (Document?)
jaroslav@1646: */
jaroslav@1646: public boolean isResolved() {
jaroslav@1646: return resolution == null;
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: private void initResolved(boolean isResolved) {
jaroslav@1646: assert(this.resolution == null); // not initialized yet!
jaroslav@1646: if (!isResolved)
jaroslav@1646: this.resolution = this;
jaroslav@1646: assert(isResolved() == isResolved);
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: void checkForTypeAlias() {
jaroslav@1646: if (isInvocable()) {
jaroslav@1646: MethodType type;
jaroslav@1646: if (this.type instanceof MethodType)
jaroslav@1646: type = (MethodType) this.type;
jaroslav@1646: else
jaroslav@1646: this.type = type = getMethodType();
jaroslav@1646: if (type.erase() == type) return;
jaroslav@1646: if (VerifyAccess.isTypeVisible(type, clazz)) return;
jaroslav@1646: throw new LinkageError("bad method type alias: "+type+" not visible from "+clazz);
jaroslav@1646: } else {
jaroslav@1646: Class> type;
jaroslav@1646: if (this.type instanceof Class>)
jaroslav@1646: type = (Class>) this.type;
jaroslav@1646: else
jaroslav@1646: this.type = type = getFieldType();
jaroslav@1646: if (VerifyAccess.isTypeVisible(type, clazz)) return;
jaroslav@1646: throw new LinkageError("bad field type alias: "+type+" not visible from "+clazz);
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646:
jaroslav@1646: /** Produce a string form of this member name.
jaroslav@1646: * For types, it is simply the type's own string (as reported by {@code toString}).
jaroslav@1646: * For fields, it is {@code "DeclaringClass.name/type"}.
jaroslav@1646: * For methods and constructors, it is {@code "DeclaringClass.name(ptype...)rtype"}.
jaroslav@1646: * If the declaring class is null, the prefix {@code "DeclaringClass."} is omitted.
jaroslav@1646: * If the member is unresolved, a prefix {@code "*."} is prepended.
jaroslav@1646: */
jaroslav@1646: @SuppressWarnings("LocalVariableHidesMemberVariable")
jaroslav@1646: @Override
jaroslav@1646: public String toString() {
jaroslav@1646: if (isType())
jaroslav@1646: return type.toString(); // class java.lang.String
jaroslav@1646: // else it is a field, method, or constructor
jaroslav@1646: StringBuilder buf = new StringBuilder();
jaroslav@1646: if (getDeclaringClass() != null) {
jaroslav@1646: buf.append(getName(clazz));
jaroslav@1646: buf.append('.');
jaroslav@1646: }
jaroslav@1646: String name = getName();
jaroslav@1646: buf.append(name == null ? "*" : name);
jaroslav@1646: Object type = getType();
jaroslav@1646: if (!isInvocable()) {
jaroslav@1646: buf.append('/');
jaroslav@1646: buf.append(type == null ? "*" : getName(type));
jaroslav@1646: } else {
jaroslav@1646: buf.append(type == null ? "(*)*" : getName(type));
jaroslav@1646: }
jaroslav@1646: byte refKind = getReferenceKind();
jaroslav@1646: if (refKind != REF_NONE) {
jaroslav@1646: buf.append('/');
jaroslav@1646: buf.append(MethodHandleNatives.refKindName(refKind));
jaroslav@1646: }
jaroslav@1646: //buf.append("#").append(System.identityHashCode(this));
jaroslav@1646: return buf.toString();
jaroslav@1646: }
jaroslav@1646: private static String getName(Object obj) {
jaroslav@1646: if (obj instanceof Class>)
jaroslav@1646: return ((Class>)obj).getName();
jaroslav@1646: return String.valueOf(obj);
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: public IllegalAccessException makeAccessException(String message, Object from) {
jaroslav@1646: message = message + ": "+ toString();
jaroslav@1646: if (from != null) message += ", from " + from;
jaroslav@1646: return new IllegalAccessException(message);
jaroslav@1646: }
jaroslav@1646: private String message() {
jaroslav@1646: if (isResolved())
jaroslav@1646: return "no access";
jaroslav@1646: else if (isConstructor())
jaroslav@1646: return "no such constructor";
jaroslav@1646: else if (isMethod())
jaroslav@1646: return "no such method";
jaroslav@1646: else
jaroslav@1646: return "no such field";
jaroslav@1646: }
jaroslav@1646: public ReflectiveOperationException makeAccessException() {
jaroslav@1646: String message = message() + ": "+ toString();
jaroslav@1646: ReflectiveOperationException ex;
jaroslav@1646: if (isResolved() || !(resolution instanceof NoSuchMethodError ||
jaroslav@1646: resolution instanceof NoSuchFieldError))
jaroslav@1646: ex = new IllegalAccessException(message);
jaroslav@1646: else if (isConstructor())
jaroslav@1646: ex = new NoSuchMethodException(message);
jaroslav@1646: else if (isMethod())
jaroslav@1646: ex = new NoSuchMethodException(message);
jaroslav@1646: else
jaroslav@1646: ex = new NoSuchFieldException(message);
jaroslav@1646: if (resolution instanceof Throwable)
jaroslav@1646: ex.initCause((Throwable) resolution);
jaroslav@1646: return ex;
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: /** Actually making a query requires an access check. */
jaroslav@1646: /*non-public*/ static Factory getFactory() {
jaroslav@1646: return Factory.INSTANCE;
jaroslav@1646: }
jaroslav@1646: /** A factory type for resolving member names with the help of the VM.
jaroslav@1646: * TBD: Define access-safe public constructors for this factory.
jaroslav@1646: */
jaroslav@1646: /*non-public*/ static class Factory {
jaroslav@1646: private Factory() { } // singleton pattern
jaroslav@1646: static Factory INSTANCE = new Factory();
jaroslav@1646:
jaroslav@1646: private static int ALLOWED_FLAGS = ALL_KINDS;
jaroslav@1646:
jaroslav@1646: /// Queries
jaroslav@1646: List getMembers(Class> defc,
jaroslav@1646: String matchName, Object matchType,
jaroslav@1646: int matchFlags, Class> lookupClass) {
jaroslav@1646: matchFlags &= ALLOWED_FLAGS;
jaroslav@1646: String matchSig = null;
jaroslav@1646: if (matchType != null) {
jaroslav@1646: matchSig = BytecodeDescriptor.unparse(matchType);
jaroslav@1646: if (matchSig.startsWith("("))
jaroslav@1646: matchFlags &= ~(ALL_KINDS & ~IS_INVOCABLE);
jaroslav@1646: else
jaroslav@1646: matchFlags &= ~(ALL_KINDS & ~IS_FIELD);
jaroslav@1646: }
jaroslav@1646: final int BUF_MAX = 0x2000;
jaroslav@1646: int len1 = matchName == null ? 10 : matchType == null ? 4 : 1;
jaroslav@1646: MemberName[] buf = newMemberBuffer(len1);
jaroslav@1646: int totalCount = 0;
jaroslav@1646: ArrayList bufs = null;
jaroslav@1646: int bufCount = 0;
jaroslav@1646: for (;;) {
jaroslav@1646: bufCount = MethodHandleNatives.getMembers(defc,
jaroslav@1646: matchName, matchSig, matchFlags,
jaroslav@1646: lookupClass,
jaroslav@1646: totalCount, buf);
jaroslav@1646: if (bufCount <= buf.length) {
jaroslav@1646: if (bufCount < 0) bufCount = 0;
jaroslav@1646: totalCount += bufCount;
jaroslav@1646: break;
jaroslav@1646: }
jaroslav@1646: // JVM returned to us with an intentional overflow!
jaroslav@1646: totalCount += buf.length;
jaroslav@1646: int excess = bufCount - buf.length;
jaroslav@1646: if (bufs == null) bufs = new ArrayList<>(1);
jaroslav@1646: bufs.add(buf);
jaroslav@1646: int len2 = buf.length;
jaroslav@1646: len2 = Math.max(len2, excess);
jaroslav@1646: len2 = Math.max(len2, totalCount / 4);
jaroslav@1646: buf = newMemberBuffer(Math.min(BUF_MAX, len2));
jaroslav@1646: }
jaroslav@1646: ArrayList result = new ArrayList<>(totalCount);
jaroslav@1646: if (bufs != null) {
jaroslav@1646: for (MemberName[] buf0 : bufs) {
jaroslav@1646: Collections.addAll(result, buf0);
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646: result.addAll(Arrays.asList(buf).subList(0, bufCount));
jaroslav@1646: // Signature matching is not the same as type matching, since
jaroslav@1646: // one signature might correspond to several types.
jaroslav@1646: // So if matchType is a Class or MethodType, refilter the results.
jaroslav@1646: if (matchType != null && matchType != matchSig) {
jaroslav@1646: for (Iterator it = result.iterator(); it.hasNext();) {
jaroslav@1646: MemberName m = it.next();
jaroslav@1646: if (!matchType.equals(m.getType()))
jaroslav@1646: it.remove();
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646: return result;
jaroslav@1646: }
jaroslav@1646: /** Produce a resolved version of the given member.
jaroslav@1646: * Super types are searched (for inherited members) if {@code searchSupers} is true.
jaroslav@1646: * Access checking is performed on behalf of the given {@code lookupClass}.
jaroslav@1646: * If lookup fails or access is not permitted, null is returned.
jaroslav@1646: * Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
jaroslav@1646: */
jaroslav@1646: private MemberName resolve(byte refKind, MemberName ref, Class> lookupClass) {
jaroslav@1646: MemberName m = ref.clone(); // JVM will side-effect the ref
jaroslav@1646: assert(refKind == m.getReferenceKind());
jaroslav@1646: try {
jaroslav@1646: m = MethodHandleNatives.resolve(m, lookupClass);
jaroslav@1646: m.checkForTypeAlias();
jaroslav@1646: m.resolution = null;
jaroslav@1646: } catch (LinkageError ex) {
jaroslav@1646: // JVM reports that the "bytecode behavior" would get an error
jaroslav@1646: assert(!m.isResolved());
jaroslav@1646: m.resolution = ex;
jaroslav@1646: return m;
jaroslav@1646: }
jaroslav@1646: assert(m.referenceKindIsConsistent());
jaroslav@1646: m.initResolved(true);
jaroslav@1646: assert(m.vminfoIsConsistent());
jaroslav@1646: return m;
jaroslav@1646: }
jaroslav@1646: /** Produce a resolved version of the given member.
jaroslav@1646: * Super types are searched (for inherited members) if {@code searchSupers} is true.
jaroslav@1646: * Access checking is performed on behalf of the given {@code lookupClass}.
jaroslav@1646: * If lookup fails or access is not permitted, a {@linkplain ReflectiveOperationException} is thrown.
jaroslav@1646: * Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
jaroslav@1646: */
jaroslav@1646: public
jaroslav@1646:
jaroslav@1646: MemberName resolveOrFail(byte refKind, MemberName m, Class> lookupClass,
jaroslav@1646: Class nsmClass)
jaroslav@1646: throws IllegalAccessException, NoSuchMemberException {
jaroslav@1646: MemberName result = resolve(refKind, m, lookupClass);
jaroslav@1646: if (result.isResolved())
jaroslav@1646: return result;
jaroslav@1646: ReflectiveOperationException ex = result.makeAccessException();
jaroslav@1646: if (ex instanceof IllegalAccessException) throw (IllegalAccessException) ex;
jaroslav@1646: throw nsmClass.cast(ex);
jaroslav@1646: }
jaroslav@1646: /** Produce a resolved version of the given member.
jaroslav@1646: * Super types are searched (for inherited members) if {@code searchSupers} is true.
jaroslav@1646: * Access checking is performed on behalf of the given {@code lookupClass}.
jaroslav@1646: * If lookup fails or access is not permitted, return null.
jaroslav@1646: * Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
jaroslav@1646: */
jaroslav@1646: public
jaroslav@1646: MemberName resolveOrNull(byte refKind, MemberName m, Class> lookupClass) {
jaroslav@1646: MemberName result = resolve(refKind, m, lookupClass);
jaroslav@1646: if (result.isResolved())
jaroslav@1646: return result;
jaroslav@1646: return null;
jaroslav@1646: }
jaroslav@1646: /** Return a list of all methods defined by the given class.
jaroslav@1646: * Super types are searched (for inherited members) if {@code searchSupers} is true.
jaroslav@1646: * Access checking is performed on behalf of the given {@code lookupClass}.
jaroslav@1646: * Inaccessible members are not added to the last.
jaroslav@1646: */
jaroslav@1646: public List getMethods(Class> defc, boolean searchSupers,
jaroslav@1646: Class> lookupClass) {
jaroslav@1646: return getMethods(defc, searchSupers, null, null, lookupClass);
jaroslav@1646: }
jaroslav@1646: /** Return a list of matching methods defined by the given class.
jaroslav@1646: * Super types are searched (for inherited members) if {@code searchSupers} is true.
jaroslav@1646: * Returned methods will match the name (if not null) and the type (if not null).
jaroslav@1646: * Access checking is performed on behalf of the given {@code lookupClass}.
jaroslav@1646: * Inaccessible members are not added to the last.
jaroslav@1646: */
jaroslav@1646: public List getMethods(Class> defc, boolean searchSupers,
jaroslav@1646: String name, MethodType type, Class> lookupClass) {
jaroslav@1646: int matchFlags = IS_METHOD | (searchSupers ? SEARCH_ALL_SUPERS : 0);
jaroslav@1646: return getMembers(defc, name, type, matchFlags, lookupClass);
jaroslav@1646: }
jaroslav@1646: /** Return a list of all constructors defined by the given class.
jaroslav@1646: * Access checking is performed on behalf of the given {@code lookupClass}.
jaroslav@1646: * Inaccessible members are not added to the last.
jaroslav@1646: */
jaroslav@1646: public List getConstructors(Class> defc, Class> lookupClass) {
jaroslav@1646: return getMembers(defc, null, null, IS_CONSTRUCTOR, lookupClass);
jaroslav@1646: }
jaroslav@1646: /** Return a list of all fields defined by the given class.
jaroslav@1646: * Super types are searched (for inherited members) if {@code searchSupers} is true.
jaroslav@1646: * Access checking is performed on behalf of the given {@code lookupClass}.
jaroslav@1646: * Inaccessible members are not added to the last.
jaroslav@1646: */
jaroslav@1646: public List getFields(Class> defc, boolean searchSupers,
jaroslav@1646: Class> lookupClass) {
jaroslav@1646: return getFields(defc, searchSupers, null, null, lookupClass);
jaroslav@1646: }
jaroslav@1646: /** Return a list of all fields defined by the given class.
jaroslav@1646: * Super types are searched (for inherited members) if {@code searchSupers} is true.
jaroslav@1646: * Returned fields will match the name (if not null) and the type (if not null).
jaroslav@1646: * Access checking is performed on behalf of the given {@code lookupClass}.
jaroslav@1646: * Inaccessible members are not added to the last.
jaroslav@1646: */
jaroslav@1646: public List getFields(Class> defc, boolean searchSupers,
jaroslav@1646: String name, Class> type, Class> lookupClass) {
jaroslav@1646: int matchFlags = IS_FIELD | (searchSupers ? SEARCH_ALL_SUPERS : 0);
jaroslav@1646: return getMembers(defc, name, type, matchFlags, lookupClass);
jaroslav@1646: }
jaroslav@1646: /** Return a list of all nested types defined by the given class.
jaroslav@1646: * Super types are searched (for inherited members) if {@code searchSupers} is true.
jaroslav@1646: * Access checking is performed on behalf of the given {@code lookupClass}.
jaroslav@1646: * Inaccessible members are not added to the last.
jaroslav@1646: */
jaroslav@1646: public List getNestedTypes(Class> defc, boolean searchSupers,
jaroslav@1646: Class> lookupClass) {
jaroslav@1646: int matchFlags = IS_TYPE | (searchSupers ? SEARCH_ALL_SUPERS : 0);
jaroslav@1646: return getMembers(defc, null, null, matchFlags, lookupClass);
jaroslav@1646: }
jaroslav@1646: private static MemberName[] newMemberBuffer(int length) {
jaroslav@1646: MemberName[] buf = new MemberName[length];
jaroslav@1646: // fill the buffer with dummy structs for the JVM to fill in
jaroslav@1646: for (int i = 0; i < length; i++)
jaroslav@1646: buf[i] = new MemberName();
jaroslav@1646: return buf;
jaroslav@1646: }
jaroslav@1646: }
jaroslav@1646:
jaroslav@1646: // static {
jaroslav@1646: // System.out.println("Hello world! My methods are:");
jaroslav@1646: // System.out.println(Factory.INSTANCE.getMethods(MemberName.class, true, null));
jaroslav@1646: // }
jaroslav@1646: }