1 /*
   2  * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package impl.java.dyn;
  27 
  28 import impl.java.dyn.util.Signatures;
  29 import java.dyn.*;
  30 import java.lang.reflect.Constructor;
  31 import java.lang.reflect.Field;
  32 import java.lang.reflect.Method;
  33 import java.lang.reflect.Member;
  34 import java.lang.reflect.Modifier;
  35 import java.util.ArrayList;
  36 import java.util.Collections;
  37 import java.util.Iterator;
  38 import java.util.List;
  39 import static impl.java.dyn.MethodHandleNatives.Constants.*;
  40 
  41 /**
  42  * Compact information which fully characterizes a method or field reference.
  43  * When resolved, it includes a direct pointer to JVM metadata.
  44  * This representation is stateless and only decriptive.
  45  * It provides no private information and no capability to use the member.
  46  * <p>
  47  * By contrast, a java.lang.reflect.Method contains fuller information
  48  * about the internals of a method (except its bytecodes) and also
  49  * allows invocation.  A MemberName is much lighter than a reflect.Method,
  50  * since it contains about 7 fields to Method's 16 (plus its sub-arrays),
  51  * and those seven fields omit much of the information in Method.
  52  * @author jrose
  53  */
  54 public final class MemberName implements Member, Cloneable {
  55     private Class<?>   clazz;       // class in which the method is defined
  56     private String     name;        // may be null if not yet materialized
  57     private Object     type;        // may be null if not yet materialized
  58     private int        flags;       // modifier bits; see reflect.Modifier
  59 
  60     private Object     vmtarget;    // VM-specific target value
  61     private int        vmindex;     // method index within class or interface
  62 
  63     { vmindex = VM_INDEX_UNINITIALIZED; }
  64 
  65     public Class<?> getDeclaringClass() {
  66         if (clazz == null && isResolved()) {
  67             expandFromVM();
  68         }
  69         return clazz;
  70     }
  71 
  72     public ClassLoader getClassLoader() {
  73         return clazz.getClassLoader();
  74     }
  75 
  76     public String getName() {
  77         if (name == null) {
  78             expandFromVM();
  79             if (name == null)  return null;
  80         }
  81         return name;
  82     }
  83 
  84     public MethodType getMethodType() {
  85         if (type == null) {
  86             expandFromVM();
  87             if (type == null)  return null;
  88         }
  89         if (!isInvocable())
  90             throw newIllegalArgumentException("not invocable, no method type");
  91         if (type instanceof MethodType) {
  92             return (MethodType) type;
  93         }
  94         if (type instanceof String) {
  95             String sig = (String) type;
  96             MethodType res = MethodType.fromBytecodeString(sig, getClassLoader());
  97             this.type = res;
  98             return res;
  99         }
 100         if (type instanceof Object[]) {
 101             Object[] typeInfo = (Object[]) type;
 102             Class<?>[] ptypes = (Class<?>[]) typeInfo[1];
 103             Class<?> rtype = (Class<?>) typeInfo[0];
 104             MethodType res = MethodType.make(rtype, ptypes);
 105             this.type = res;
 106             return res;
 107         }
 108         throw new InternalError("bad method type "+type);
 109     }
 110 
 111     public MethodType getInvocationType() {
 112         MethodType itype = getMethodType();
 113         if (!isStatic())
 114             itype = itype.insertParameterType(0, clazz);
 115         return itype;
 116     }
 117 
 118     public Class<?>[] getParameterTypes() {
 119         return getMethodType().parameterArray();
 120     }
 121 
 122     public Class<?> getReturnType() {
 123         return getMethodType().returnType();
 124     }
 125 
 126     public Class<?> getFieldType() {
 127         if (type == null) {
 128             expandFromVM();
 129             if (type == null)  return null;
 130         }
 131         if (isInvocable())
 132             throw newIllegalArgumentException("not a field or nested class, no simple type");
 133         if (type instanceof Class<?>) {
 134             return (Class<?>) type;
 135         }
 136         if (type instanceof String) {
 137             String sig = (String) type;
 138             MethodType mtype = MethodType.fromBytecodeString("()"+sig, getClassLoader());
 139             Class<?> res = mtype.returnType();
 140             this.type = res;
 141             return res;
 142         }
 143         throw new InternalError("bad field type "+type);
 144     }
 145 
 146     public Object getType() {
 147         return (isInvocable() ? getMethodType() : getFieldType());
 148     }
 149 
 150     public String getSignature() {
 151         if (type == null) {
 152             expandFromVM();
 153             if (type == null)  return null;
 154         }
 155         if (type instanceof String)
 156             return (String) type;
 157         if (isInvocable())
 158             return Signatures.unparse(getMethodType());
 159         else
 160             return Signatures.unparse(getFieldType());
 161     }
 162 
 163     public int getModifiers() {
 164         return (flags & RECOGNIZED_MODIFIERS);
 165     }
 166 
 167     private void setFlags(int flags) {
 168         this.flags = flags;
 169         assert(testAnyFlags(ALL_KINDS));
 170     }
 171 
 172     private boolean testFlags(int mask, int value) {
 173         return (flags & mask) == value;
 174     }
 175     private boolean testAllFlags(int mask) {
 176         return testFlags(mask, mask);
 177     }
 178     private boolean testAnyFlags(int mask) {
 179         return !testFlags(mask, 0);
 180     }
 181 
 182     public boolean isStatic() {
 183         return Modifier.isStatic(flags);
 184     }
 185     public boolean isPublic() {
 186         return Modifier.isPublic(flags);
 187     }
 188     public boolean isPrivate() {
 189         return Modifier.isPrivate(flags);
 190     }
 191     public boolean isProtected() {
 192         return Modifier.isProtected(flags);
 193     }
 194     public boolean isFinal() {
 195         return Modifier.isFinal(flags);
 196     }
 197     public boolean isAbstract() {
 198         return Modifier.isAbstract(flags);
 199     }
 200     // let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo
 201 
 202     // unofficial modifier flags, used by HotSpot:
 203     static final int BRIDGE    = 0x00000040;
 204     static final int VARARGS   = 0x00000080;
 205     static final int SYNTHETIC = 0x00001000;
 206     static final int ANNOTATION= 0x00002000;
 207     static final int ENUM      = 0x00004000;
 208     public boolean isBridge() {
 209         return testAllFlags(IS_METHOD | BRIDGE);
 210     }
 211     public boolean isVarargs() {
 212         return testAllFlags(VARARGS) && isInvocable();
 213     }
 214     public boolean isSynthetic() {
 215         return testAllFlags(SYNTHETIC);
 216     }
 217 
 218     static final String CONSTRUCTOR_NAME = "<init>";  // the ever-popular
 219 
 220     // modifiers exported by the JVM:
 221     static final int RECOGNIZED_MODIFIERS = 0xFFFF;
 222 
 223     // private flags, not part of RECOGNIZED_MODIFIERS:
 224     static final int
 225             IS_METHOD      = MN_IS_METHOD,      // method (not constructor)
 226             IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor
 227             IS_FIELD       = MN_IS_FIELD,       // field
 228             IS_TYPE        = MN_IS_TYPE;        // nested type
 229     static final int  // for MethodHandleNatives.getMembers
 230             SEARCH_SUPERCLASSES = MN_SEARCH_SUPERCLASSES,
 231             SEARCH_INTERFACES   = MN_SEARCH_INTERFACES;
 232 
 233     static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED;
 234     static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE;
 235     static final int IS_INVOCABLE = IS_METHOD | IS_CONSTRUCTOR;
 236     static final int IS_FIELD_OR_METHOD = IS_METHOD | IS_FIELD;
 237     static final int SEARCH_ALL_SUPERS = SEARCH_SUPERCLASSES | SEARCH_INTERFACES;
 238 
 239     public boolean isInvocable() {
 240         return testAnyFlags(IS_INVOCABLE);
 241     }
 242     public boolean isFieldOrMethod() {
 243         return testAnyFlags(IS_FIELD_OR_METHOD);
 244     }
 245     public boolean isMethod() {
 246         return testAllFlags(IS_METHOD);
 247     }
 248     public boolean isConstructor() {
 249         return testAllFlags(IS_CONSTRUCTOR);
 250     }
 251     public boolean isField() {
 252         return testAllFlags(IS_FIELD);
 253     }
 254     public boolean isType() {
 255         return testAllFlags(IS_TYPE);
 256     }
 257     public boolean isPackage() {
 258         return !testAnyFlags(ALL_ACCESS);
 259     }
 260 
 261     /** Initialize a query.   It is not resolved. */
 262     private void init(Class<?> defClass, String name, Object type, int flags) {
 263         // defining class is allowed to be null (for a naked name/type pair)
 264         name.toString();  // null check
 265         type.equals(type);  // null check
 266         // fill in fields:
 267         this.clazz = defClass;
 268         this.name = name;
 269         this.type = type;
 270         setFlags(flags);
 271         assert(!isResolved());
 272     }
 273 
 274     private void expandFromVM() {
 275         if (!isResolved())  return;
 276         if (type instanceof Object[])
 277             type = null;  // don't saddle JVM w/ typeInfo
 278         MethodHandleNatives.expand(this);
 279     }
 280 
 281     // Capturing information from the Core Reflection API:
 282     private static int flagsMods(int flags, int mods) {
 283         assert((flags & RECOGNIZED_MODIFIERS) == 0);
 284         assert((mods & ~RECOGNIZED_MODIFIERS) == 0);
 285         return flags | mods;
 286     }
 287     public MemberName(Method m) {
 288         Object[] typeInfo = { m.getReturnType(), m.getParameterTypes() };
 289         init(m.getDeclaringClass(), m.getName(), typeInfo, flagsMods(IS_METHOD, m.getModifiers()));
 290         // fill in vmtarget, vmindex while we have m in hand:
 291         MethodHandleNatives.init(this, m);
 292         assert(isResolved());
 293     }
 294     public MemberName(Constructor ctor) {
 295         Object[] typeInfo = { void.class, ctor.getParameterTypes() };
 296         init(ctor.getDeclaringClass(), CONSTRUCTOR_NAME, typeInfo, flagsMods(IS_CONSTRUCTOR, ctor.getModifiers()));
 297         // fill in vmtarget, vmindex while we have ctor in hand:
 298         MethodHandleNatives.init(this, ctor);
 299         assert(isResolved());
 300     }
 301     public MemberName(Field fld) {
 302         init(fld.getDeclaringClass(), fld.getName(), fld.getType(), flagsMods(IS_FIELD, fld.getModifiers()));
 303         // fill in vmtarget, vmindex while we have fld in hand:
 304         MethodHandleNatives.init(this, fld);
 305         assert(isResolved());
 306     }
 307     public MemberName(Class<?> type) {
 308         init(type.getDeclaringClass(), type.getSimpleName(), type, flagsMods(IS_TYPE, type.getModifiers()));
 309         vmindex = 0;  // isResolved
 310         assert(isResolved());
 311     }
 312 
 313     // bare-bones constructor; the JVM will fill it in
 314     MemberName() { }
 315 
 316     // locally useful cloner
 317     @Override protected MemberName clone() {
 318         try {
 319             return (MemberName) super.clone();
 320         } catch (CloneNotSupportedException ex) {
 321             throw new InternalError();
 322         }
 323      }
 324 
 325     // %%% define equals/hashcode?
 326 
 327     // Construction from symbolic parts, for queries:
 328     public MemberName(Class<?> defClass, String name, Class<?> type, int modifiers) {
 329         init(defClass, name, type, IS_FIELD | (modifiers & RECOGNIZED_MODIFIERS));
 330     }
 331     public MemberName(Class<?> defClass, String name, Class<?> type) {
 332         this(defClass, name, type, 0);
 333     }
 334     public MemberName(Class<?> defClass, String name, MethodType type, int modifiers) {
 335         int flagBit = (name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
 336         init(defClass, name, type, flagBit | (modifiers & RECOGNIZED_MODIFIERS));
 337     }
 338     public MemberName(Class<?> defClass, String name, MethodType type) {
 339         this(defClass, name, type, 0);
 340     }
 341 
 342     boolean isResolved() {
 343         return (vmindex != VM_INDEX_UNINITIALIZED);
 344     }
 345 
 346     public boolean hasReceiverTypeDispatch() {
 347         return (isMethod() && getVMIndex(Access.TOKEN) >= 0);
 348     }
 349 
 350     @Override
 351     public String toString() {
 352         if (isType())
 353             return type.toString();  // class java.lang.String
 354         // else it is a field, method, or constructor
 355         StringBuilder buf = new StringBuilder();
 356         if (getDeclaringClass() != null) {
 357             buf.append(getName(clazz));
 358             buf.append('.');
 359         }
 360         buf.append(getName());
 361         if (!isInvocable())  buf.append('/');
 362         buf.append(getName(getType()));
 363         /*
 364         buf.append('/');
 365         // key: Public, private, pRotected, sTatic, Final, sYnchronized,
 366         // transient/Varargs, native, (interface), abstract, sTrict, sYnthetic,
 367         // (annotation), Enum, (unused)
 368         final String FIELD_MOD_CHARS  = "PprTF?vt????Y?E?";
 369         final String METHOD_MOD_CHARS = "PprTFybVn?atY???";
 370         String modChars = (isInvocable() ? METHOD_MOD_CHARS : FIELD_MOD_CHARS);
 371         for (int i = 0; i < modChars.length(); i++) {
 372             if ((flags & (1 << i)) != 0) {
 373                 char mc = modChars.charAt(i);
 374                 if (mc != '.')
 375                     buf.append(mc);
 376             }
 377         }
 378          */
 379         return buf.toString();
 380     }
 381     private static String getName(Object obj) {
 382         if (obj instanceof Class<?>)
 383             return ((Class<?>)obj).getName();
 384         return obj.toString();
 385     }
 386 
 387     // Queries to the JVM:
 388     public int getVMIndex(Access token) {
 389         Access.check(token);
 390         if (!isResolved())
 391             throw newIllegalStateException("not resolved");
 392         return vmindex;
 393     }
 394 //    public Object getVMTarget(Access token) {
 395 //        Access.check(token);
 396 //        if (!isResolved())
 397 //            throw newIllegalStateException("not resolved");
 398 //        return vmtarget;
 399 //    }
 400     private RuntimeException newIllegalStateException(String message) {
 401         return new IllegalStateException(message+": "+this);
 402     }
 403 
 404     // handy shared exception makers (they simplify the common case code)
 405     public static RuntimeException newIllegalArgumentException(String message) {
 406         return new IllegalArgumentException(message);
 407     }
 408     public static NoAccessException newNoAccessException(MemberName name, Class<?> caller) {
 409         return newNoAccessException("cannot access", name, caller);
 410     }
 411     public static NoAccessException newNoAccessException(String message,
 412             MemberName name, Class<?> caller) {
 413         message += ": " + name;
 414         if (caller != null)  message += ", from " + caller.getName();
 415         return new NoAccessException(message);
 416     }
 417 
 418     /** Actually making a query requires an access check. */
 419     public static Factory getFactory(Access token) {
 420         Access.check(token);
 421         return Factory.INSTANCE;
 422     }
 423     public static Factory getFactory() {
 424         return getFactory(Access.getToken());
 425     }
 426     public static class Factory {
 427         private Factory() { } // singleton pattern
 428         static Factory INSTANCE = new Factory();
 429 
 430         private static int ALLOWED_FLAGS = SEARCH_ALL_SUPERS | ALL_KINDS;
 431 
 432         /// Queries
 433         List<MemberName> getMembers(Class<?> defc,
 434                 String matchName, Object matchType,
 435                 int matchFlags, Class<?> caller) {
 436             matchFlags &= ALLOWED_FLAGS;
 437             String matchSig = null;
 438             if (matchType != null) {
 439                 matchSig = Signatures.unparse(matchType);
 440                 if (matchSig.startsWith("("))
 441                     matchFlags &= ~(ALL_KINDS & ~IS_INVOCABLE);
 442                 else
 443                     matchFlags &= ~(ALL_KINDS & ~IS_FIELD);
 444             }
 445             final int BUF_MAX = 0x2000;
 446             int len1 = matchName == null ? 10 : matchType == null ? 4 : 1;
 447             MemberName[] buf = newMemberBuffer(len1);
 448             int totalCount = 0;
 449             ArrayList<MemberName[]> bufs = null;
 450             for (;;) {
 451                 int bufCount = MethodHandleNatives.getMembers(defc,
 452                         matchName, matchSig, matchFlags, caller,
 453                         totalCount, buf);
 454                 if (bufCount <= buf.length) {
 455                     if (bufCount >= 0)
 456                         totalCount += bufCount;
 457                     break;
 458                 }
 459                 // JVM returned tp us with an intentional overflow!
 460                 totalCount += buf.length;
 461                 int excess = bufCount - buf.length;
 462                 if (bufs == null)  bufs = new ArrayList<MemberName[]>(1);
 463                 bufs.add(buf);
 464                 int len2 = buf.length;
 465                 len2 = Math.max(len2, excess);
 466                 len2 = Math.max(len2, totalCount / 4);
 467                 buf = newMemberBuffer(Math.min(BUF_MAX, len2));
 468             }
 469             ArrayList<MemberName> result = new ArrayList<MemberName>(totalCount);
 470             if (bufs != null) {
 471                 for (MemberName[] buf0 : bufs) {
 472                     Collections.addAll(result, buf0);
 473                 }
 474             }
 475             Collections.addAll(result, buf);
 476             // Signature matching is not the same as type matching, since
 477             // one signature might correspond to several types.
 478             // So if matchType is a Class or MethodType, refilter the results.
 479             if (matchType != null && matchType != matchSig) {
 480                 for (Iterator<MemberName> it = result.iterator(); it.hasNext();) {
 481                     MemberName m = it.next();
 482                     if (!matchType.equals(m.getType()))
 483                         it.remove();
 484                 }
 485             }
 486             return result;
 487         }
 488         boolean resolveInPlace(MemberName m, boolean searchSupers, Class<?> caller) {
 489             MethodHandleNatives.resolve(m, caller);
 490             if (m.isResolved())  return true;
 491             int matchFlags = m.flags | (searchSupers ? SEARCH_ALL_SUPERS : 0);
 492             String matchSig = m.getSignature();
 493             MemberName[] buf = { m };
 494             int n = MethodHandleNatives.getMembers(m.getDeclaringClass(),
 495                     m.getName(), matchSig, matchFlags, caller, 0, buf);
 496             if (n != 1)  return false;
 497             return m.isResolved();
 498         }
 499         public MemberName resolveOrNull(MemberName m, boolean searchSupers, Class<?> caller) {
 500             MemberName result = m.clone();
 501             if (resolveInPlace(result, searchSupers, caller))
 502                 return result;
 503             return null;
 504         }
 505         public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> caller) {
 506             MemberName result = resolveOrNull(m, searchSupers, caller);
 507             if (result != null)
 508                 return result;
 509             throw newNoAccessException(m, caller);
 510         }
 511         public List<MemberName> getMethods(Class<?> defc, boolean searchSupers,
 512                 Class<?> caller) {
 513             return getMethods(defc, searchSupers, null, null, caller);
 514         }
 515         public List<MemberName> getMethods(Class<?> defc, boolean searchSupers,
 516                 String name, MethodType type, Class<?> caller) {
 517             int matchFlags = IS_METHOD | (searchSupers ? SEARCH_ALL_SUPERS : 0);
 518             return getMembers(defc, name, type, matchFlags, caller);
 519         }
 520         public List<MemberName> getConstructors(Class<?> defc, Class<?> caller) {
 521             return getMembers(defc, null, null, IS_CONSTRUCTOR, caller);
 522         }
 523         public List<MemberName> getFields(Class<?> defc, boolean searchSupers,
 524                 Class<?> caller) {
 525             return getFields(defc, searchSupers, null, null, caller);
 526         }
 527         public List<MemberName> getFields(Class<?> defc, boolean searchSupers,
 528                 String name, Class<?> type, Class<?> caller) {
 529             int matchFlags = IS_FIELD | (searchSupers ? SEARCH_ALL_SUPERS : 0);
 530             return getMembers(defc, name, type, matchFlags, caller);
 531         }
 532         public List<MemberName> getNestedTypes(Class<?> defc, boolean searchSupers,
 533                 Class<?> caller) {
 534             int matchFlags = IS_TYPE | (searchSupers ? SEARCH_ALL_SUPERS : 0);
 535             return getMembers(defc, null, null, matchFlags, caller);
 536         }
 537         private static MemberName[] newMemberBuffer(int length) {
 538             MemberName[] buf = new MemberName[length];
 539             // fill the buffer with dummy structs for the JVM to fill in
 540             for (int i = 0; i < length; i++)
 541                 buf[i] = new MemberName();
 542             return buf;
 543         }
 544     }
 545 
 546 //    static {
 547 //        System.out.println("Hello world!  My methods are:");
 548 //        System.out.println(Factory.INSTANCE.getMethods(MemberName.class, true, null));
 549 //    }
 550 }