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 java.dyn;
  27 
  28 import java.util.Arrays;
  29 import java.util.Collections;
  30 import java.util.HashMap;
  31 import java.util.List;
  32 import impl.java.dyn.*;
  33 import impl.java.dyn.util.Signatures;
  34 import static impl.java.dyn.MemberName.newIllegalArgumentException;
  35 
  36 /**
  37  * Run-time token used to match call sites with method handles.
  38  * The structure is a return type accompanied by any number of parameter types.
  39  * The types (primitive, void, and reference) are represented by Class objects.
  40  * All instances of <code>MethodType</code> are immutable.
  41  * Two instances are completely interchangeable if they compare equal.
  42  * Equality depends exactly on the return and parameter types.
  43  * <p>
  44  * This type can be created only by factory methods, which manage interning.
  45  *
  46  * @author John Rose, JSR 292 EG
  47  */
  48 public final
  49 class MethodType {
  50     final Class<?>   rtype;
  51     final Class<?>[] ptypes;
  52     MethodTypeForm form; // erased form, plus cached data about primitives
  53     MethodType wrapAlt;  // alternative wrapped/unwrapped version
  54 
  55     private static final Access IMPL_TOKEN = Access.getToken();
  56 
  57     private MethodType(Class<?> rtype, Class<?>[] ptypes) {
  58         checkRtype(rtype);
  59         checkPtypes(ptypes);
  60         this.rtype = rtype;
  61         this.ptypes = ptypes;
  62     }
  63 
  64     private void checkRtype(Class<?> rtype) {
  65         rtype.equals(rtype);  // null check
  66     }
  67     private void checkPtypes(Class<?>[] ptypes) {
  68         for (Class<?> ptype : ptypes) {
  69             ptype.equals(ptype);  // null check
  70             if (ptype == void.class)
  71                 throw newIllegalArgumentException("void parameter: "+this);
  72         }
  73     }
  74 
  75     static final HashMap<MethodType,MethodType> internTable
  76             = new HashMap<MethodType, MethodType>();
  77 
  78     static final Class<?>[] NO_PTYPES = {};
  79 
  80     /** Find or create an instance of the given method type.
  81      * @param rtype  the return type
  82      * @param ptypes the parameter types
  83      * @return the interned method type with the given parts
  84      * @throws NullPointerException if rtype or any ptype is null
  85      * @throws IllegalArgumentException if any of the ptypes is void
  86      */
  87     public static
  88     MethodType make(Class<?> rtype, Class<?>[] ptypes) {
  89         return makeImpl(rtype, ptypes, false);
  90     }
  91 
  92     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. */
  93     public static
  94     MethodType make(Class<?> rtype, List<Class<?>> ptypes) {
  95         return makeImpl(rtype, ptypes.toArray(NO_PTYPES), true);
  96     }
  97 
  98     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
  99      *  The leading parameter type is prepended to the remaining array.
 100      */
 101     public static
 102     MethodType make(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
 103         Class<?>[] ptypes1 = new Class<?>[1+ptypes.length];
 104         ptypes1[0] = ptype0;
 105         System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length);
 106         return makeImpl(rtype, ptypes1, true);
 107     }
 108 
 109     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
 110      *  The resulting method has no parameter types.
 111      */
 112     public static
 113     MethodType make(Class<?> rtype) {
 114         return makeImpl(rtype, NO_PTYPES, true);
 115     }
 116 
 117     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
 118      *  The resulting method has the single given parameter type.
 119      */
 120     public static
 121     MethodType make(Class<?> rtype, Class<?> ptype0) {
 122         return makeImpl(rtype, new Class<?>[]{ ptype0 }, true);
 123     }
 124 
 125     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
 126      *  The resulting method has the same parameter types as {@code ptypes},
 127      *  and the specified return type.
 128      */
 129     public static
 130     MethodType make(Class<?> rtype, MethodType ptypes) {
 131         return makeImpl(rtype, ptypes.ptypes, true);
 132     }
 133 
 134     /**
 135      * Sole factory method to find or create an interned method type.
 136      * @param rtype desired return type
 137      * @param ptypes desired parameter types
 138      * @param trusted whether the ptypes can be used without cloning
 139      * @return the unique method type of the desired structure
 140      */
 141     static
 142     MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
 143         if (ptypes == null || ptypes.length == 0) {
 144             ptypes = NO_PTYPES; trusted = true;
 145         }
 146         MethodType mt1 = new MethodType(rtype, ptypes);
 147         MethodType mt0;
 148         synchronized (internTable) {
 149             mt0 = internTable.get(mt1);
 150             if (mt0 != null)
 151                 return mt0;
 152         }
 153         if (!trusted)
 154             // defensively copy the array passed in by the user
 155             mt1 = new MethodType(rtype, ptypes.clone());
 156         // promote the object to the Real Thing, and reprobe
 157         mt1.form = MethodTypeForm.findForm(mt1);
 158         if (mt1.form.erasedType == mt1)
 159             MethodHandleImpl.init(IMPL_TOKEN, mt1);
 160         synchronized (internTable) {
 161             mt0 = internTable.get(mt1);
 162             if (mt0 != null)
 163                 return mt0;
 164             internTable.put(mt1, mt1);
 165         }
 166         return mt1;
 167     }
 168 
 169     private static final MethodType[] objectOnlyTypes = new MethodType[20];
 170 
 171     /**
 172      * Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
 173      * All parameters and the return type will be Object, except the final varargs parameter if any.
 174      * @param objectArgCount number of parameters (excluding the varargs parameter if any)
 175      * @param varargs whether there will be a varargs parameter
 176      * @return a totally generic method type, given only its count of parameters and varargs
 177      * @see #makeGeneric(int)
 178      */
 179     public static
 180     MethodType makeGeneric(int objectArgCount, boolean varargs) {
 181         MethodType mt;
 182         int ivarargs = (!varargs ? 0 : 1);
 183         int ootIndex = objectArgCount*2 + ivarargs;
 184         if (ootIndex < objectOnlyTypes.length) {
 185             mt = objectOnlyTypes[ootIndex];
 186             if (mt != null)  return mt;
 187         }
 188         Class<?>[] ptypes = new Class<?>[objectArgCount + ivarargs];
 189         Arrays.fill(ptypes, Object.class);
 190         if (ivarargs != 0)  ptypes[objectArgCount] = Object[].class;
 191         mt = makeImpl(Object.class, ptypes, true);
 192         if (ootIndex < objectOnlyTypes.length) {
 193             objectOnlyTypes[ootIndex] = mt;     // cache it here also!
 194         }
 195         return mt;
 196     }
 197 
 198     /**
 199      * All parameters and the return type will be Object.
 200      * @param objectArgCount number of parameters
 201      * @return a totally generic method type, given only its count of parameters
 202      * @see #makeGeneric(int, boolean)
 203      */
 204     public static
 205     MethodType makeGeneric(int objectArgCount) {
 206         return makeGeneric(objectArgCount, false);
 207     }
 208 
 209     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
 210      * @param num    the index (zero-based) of the parameter type to change
 211      * @param nptype a new parameter type to replace the old one with
 212      * @return the same type, except with the selected parameter changed
 213      */
 214     public MethodType changeParameterType(int num, Class<?> nptype) {
 215         if (parameterType(num) == nptype)  return this;
 216         Class<?>[] nptypes = ptypes.clone();
 217         nptypes[num] = nptype;
 218         return makeImpl(rtype, nptypes, true);
 219     }
 220 
 221     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
 222      * @param num    the position (zero-based) of the inserted parameter type
 223      * @param nptype a new parameter type to insert into the parameter list
 224      * @return the same type, except with the selected parameter inserted
 225      */
 226     public MethodType insertParameterType(int num, Class<?> nptype) {
 227         int len = ptypes.length;
 228         Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+1);
 229         System.arraycopy(nptypes, num, nptypes, num+1, len-num);
 230         nptypes[num] = nptype;
 231         return makeImpl(rtype, nptypes, true);
 232     }
 233 
 234     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
 235      * @param num    the index (zero-based) of the parameter type to remove
 236      * @return the same type, except with the selected parameter removed
 237      */
 238     public MethodType deleteParameterType(int num) {
 239         int len = ptypes.length;
 240         Class<?>[] nptypes;
 241         if (num == 0) {
 242             nptypes = Arrays.copyOfRange(ptypes, 1, len);
 243         } else {
 244             nptypes = Arrays.copyOfRange(ptypes, 0, len-1);
 245             System.arraycopy(ptypes, num+1, nptypes, num, (len-1)-num);
 246         }
 247         return makeImpl(rtype, nptypes, true);
 248     }
 249 
 250     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
 251      * @param nrtype a return parameter type to replace the old one with
 252      * @return the same type, except with the return type change
 253      */
 254     public MethodType changeReturnType(Class<?> nrtype) {
 255         if (returnType() == nrtype)  return this;
 256         return makeImpl(nrtype, ptypes, true);
 257     }
 258 
 259     /** Convenience method.
 260      * Report if this type contains a primitive argument or return value.
 261      * @return true if any of the types are primitives
 262      */
 263     public boolean hasPrimitives() {
 264         return form.primCounts != 0;
 265     }
 266 
 267     /** Convenience method.
 268      * Report if this type contains a wrapper argument or return value.
 269      * Wrappers are types which box primitive values, such as {@link Integer}.
 270      * @return true if any of the types are wrappers
 271      */
 272     public boolean hasWrappers() {
 273         return unwrap() != this;
 274     }
 275 
 276     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
 277      * Erase all reference types to Object.
 278      * @return a version of the original type with all reference types replaced
 279      */
 280     public MethodType erase() {
 281         return form.erasedType;
 282     }
 283 
 284     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
 285      * Erase all reference types to Object, and all primitive types to wrappers.
 286      * @return a version of the original type with references erasedType and primitives wrapped
 287      */
 288     public MethodType eraseWrap() {
 289         return form.wrappedType;
 290     }
 291 
 292     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
 293      * Convert all types, both reference and primitive, to Object.
 294      * @return a version of the original type with all types replaced
 295      * @see #makeGeneric(int)
 296      */
 297     public MethodType generic() {
 298         return form.wrappedType.erase();
 299     }
 300 
 301     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
 302      * Convert all primitive types to their corresponding wrapper types.
 303      * @return a version of the original type with all primitive types replaced
 304      */
 305     public MethodType wrap() {
 306         return hasPrimitives() ? wrapWithPrims(this) : this;
 307     }
 308 
 309     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
 310      * Convert all wrapper types to their corresponding primitive types.
 311      * @return a version of the original type with all wrapper types replaced
 312      */
 313     public MethodType unwrap() {
 314         MethodType noprims = !hasPrimitives() ? this : wrapWithPrims(this);
 315         return unwrapWithNoPrims(noprims);
 316     }
 317 
 318     private static MethodType wrapWithPrims(MethodType pt) {
 319         assert(pt.hasPrimitives());
 320         MethodType wt = pt.wrapAlt;
 321         if (wt == null) {
 322             // fill in lazily
 323             wt = MethodTypeForm.canonType(pt, MethodTypeForm.WRAP);
 324             assert(wt != null);
 325             pt.wrapAlt = wt;
 326         }
 327         return wt;
 328     }
 329 
 330     private static MethodType unwrapWithNoPrims(MethodType wt) {
 331         assert(!wt.hasPrimitives());
 332         MethodType uwt = wt.wrapAlt;
 333         if (uwt == null) {
 334             // fill in lazily
 335             uwt = MethodTypeForm.canonType(wt, MethodTypeForm.UNWRAP);
 336             if (uwt == null)
 337                 uwt = wt;    // type has no wrappers or prims at all
 338             wt.wrapAlt = uwt;
 339         }
 340         return uwt;
 341     }
 342 
 343     /** @param num the index (zero-based) of the desired parameter type
 344      *  @return the selected parameter type
 345      */
 346     public Class<?> parameterType(int num) {
 347         return ptypes[num];
 348     }
 349     /** @return the number of parameter types */
 350     public int parameterCount() {
 351         return ptypes.length;
 352     }
 353     /** @return the return type */
 354     public Class<?> returnType() {
 355         return rtype;
 356     }
 357 
 358     /**
 359      * Convenience method to present the arguments as a list.
 360      * @return the parameter types (as an immutable list)
 361      */
 362     public List<Class<?>> parameterList() {
 363         return Collections.unmodifiableList(Arrays.asList(ptypes));
 364     }
 365 
 366     /**
 367      * Convenience method to present the arguments as an array.
 368      * @return the parameter types (as a fresh copy if necessary)
 369      */
 370     public Class<?>[] parameterArray() {
 371         return ptypes.clone();
 372     }
 373 
 374     /**
 375      * Compares the specified object with this type for equality.
 376      * That is, it returns <tt>true</tt> if and only if the specified object
 377      * is also a method type with exactly the same parameters and return type.
 378      * @param x object to compare
 379      * @see Object#equals(Object)
 380      */
 381     @Override
 382     public boolean equals(Object x) {
 383         return this == x || x instanceof MethodType && equals((MethodType)x);
 384     }
 385 
 386     private boolean equals(MethodType that) {
 387         return this.rtype == that.rtype
 388             && Arrays.equals(this.ptypes, that.ptypes);
 389     }
 390 
 391     /**
 392      * Returns the hash code value for this method type.
 393      * It is defined to be the same as the hashcode of a List
 394      * whose elements are the return type followed by the
 395      * parameter types.
 396      * @return the hash code value for this method type
 397      * @see Object#hashCode()
 398      * @see #equals(Object)
 399      * @see List#hashCode()
 400      */
 401     @Override
 402     public int hashCode() {
 403       int hashCode = 31 + rtype.hashCode();
 404       for (Class<?> ptype : ptypes)
 405           hashCode = 31*hashCode + ptype.hashCode();
 406       return hashCode;
 407     }
 408 
 409     /**
 410      * The string representation of a method type is a
 411      * parenthesis enclosed, comma separated list of type names,
 412      * followed immediately by the return type.
 413      * <p>
 414      * If a type name is array, it the base type followed
 415      * by [], rather than the Class.getName of the array type.
 416      */
 417     @Override
 418     public String toString() {
 419         StringBuilder sb = new StringBuilder();
 420         sb.append("(");
 421         for (int i = 0; i < ptypes.length; i++) {
 422             if (i > 0)  sb.append(",");
 423         }
 424         sb.append(")");
 425         putName(sb, rtype);
 426         return sb.toString();
 427     }
 428 
 429     static void putName(StringBuilder sb, Class<?> cls) {
 430         int brackets = 0;
 431         while (cls.isArray()) {
 432             cls = cls.getComponentType();
 433             brackets++;
 434         }
 435         String n = cls.getName();
 436         /*
 437         if (n.startsWith("java.lang.")) {
 438             String nb = n.substring("java.lang.".length());
 439             if (nb.indexOf('.') < 0)  n = nb;
 440         } else if (n.indexOf('.') < 0) {
 441             n = "."+n;          // anonymous package
 442         }
 443         */
 444         sb.append(n);
 445         while (brackets > 0) {
 446             sb.append("[]");
 447             brackets--;
 448         }
 449     }
 450 
 451     /// Queries which have to do with the bytecode architecture
 452 
 453     /** The number of JVM stack slots required to invoke a method
 454      * of this type.  Note that (for historic reasons) the JVM requires
 455      * a second stack slot to pass long and double arguments.
 456      * So this method returns {@link #parameterCount()} plus the
 457      * number of long and double parameters (if any).
 458      * @return the number of JVM stack slots for this type's parameters
 459      */
 460     public int parameterSlotCount() {
 461         return form.parameterSlotCount();
 462     }
 463 
 464     /** The JVM stack slot which carries the given parameter.
 465      * The last parameter ({@link #parameterCount()}-1) is carried in the
 466      * the most shallowly stacked argument slot, which is numbered as slot zero.
 467      * The first parameter is carried in the most deeply stacked argument slot,
 468      * which is {@link #parameterSlotCount()} minus the number of slots used
 469      * by that parameter.
 470      * <p>
 471      * As a useful edge case, {@code parameterSlot(-1)} returns
 472      * {@link #parameterSlotCount()}.  No other negative values are accepted.
 473      * <p>
 474      * In general, the number returned is the number of arguments <em>after</em>
 475      * the given parameter, <em>plus</em> the number of long or double arguments
 476      * after the argument for the given parameter.
 477      * @param num the index (zero-based) of the desired parameter type
 478      * @return the index of the (shallowest) JVM stack slot transmitting the
 479      *         given parameter
 480      */
 481     public int parameterSlot(int num) {
 482         return form.parameterToArgSlot(num);
 483     }
 484 
 485     /** The number of JVM stack slots required to receive a return value
 486      * from a method of this type.
 487      * If the {@link #returnType() return type} is void, it will be zero,
 488      * else if the return type is long or double, it will be two, else one.
 489      * @return the number of JVM stack slots (0, 1, or 2) for this type's return value
 490      */
 491     public int returnSlotCount() {
 492         return form.returnSlotCount();
 493     }
 494 
 495     /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
 496      * Find or create an instance (interned) of the given method type.
 497      * Any class or interface name embedded in the signature string
 498      * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)}
 499      * on the given loader (or if it is null, on the system class loader).
 500      * <p>
 501      * Note that it is possible to build method types which cannot be
 502      * constructed by this method, because their component types are
 503      * not all reachable from a common class loader.
 504      * @param bytecodeSignature a bytecode-level signature string "(T...)T"
 505      * @param loader the class loader in which to look up the types
 506      * @return a method type matching the bytecode-level signature
 507      * @throws IllegalArgumentException if the string is not well-formed
 508      * @throws TypeNotPresentException if a named type cannot be found
 509      */
 510     public static MethodType fromBytecodeString(String bytecodeSignature, ClassLoader loader)
 511         throws IllegalArgumentException, TypeNotPresentException
 512     {
 513         List<Class<?>> types = Signatures.parseMethod(bytecodeSignature, loader);
 514         Class<?> rtype = types.remove(types.size() - 1);
 515         Class<?>[] ptypes = types.toArray(NO_PTYPES);
 516         return makeImpl(rtype, ptypes, true);
 517     }
 518 
 519     /**
 520      * Create a bytecode signature representation of the type.
 521      * Note that this is not a strict inverse of
 522      * {@link #fromBytecodeString(java.lang.String, java.lang.ClassLoader)},
 523      * because the latter requires a suitable class loader argument.
 524      * @return the bytecode signature representation
 525      */
 526     public String toBytecodeString() {
 527         return Signatures.unparse(this);
 528     }
 529 }