| /* |
| * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| */ |
| |
| package java.dyn; |
| |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import sun.dyn.Access; |
| import sun.dyn.Invokers; |
| import sun.dyn.MethodTypeImpl; |
| import sun.dyn.util.BytecodeSignature; |
| import static sun.dyn.MemberName.newIllegalArgumentException; |
| |
| /** |
| * Run-time token used to match call sites with method handles. |
| * The structure is a return type accompanied by any number of parameter types. |
| * The types (primitive, void, and reference) are represented by Class objects. |
| * All instances of <code>MethodType</code> are immutable. |
| * Two instances are completely interchangeable if they compare equal. |
| * Equality depends exactly on the return and parameter types. |
| * <p> |
| * This type can be created only by factory methods, which manage interning. |
| * |
| * @author John Rose, JSR 292 EG |
| */ |
| public final |
| class MethodType { |
| private final Class<?> rtype; |
| private final Class<?>[] ptypes; |
| private MethodTypeForm form; // erased form, plus cached data about primitives |
| private MethodType wrapAlt; // alternative wrapped/unwrapped version |
| private Invokers invokers; // cache of handy higher-order adapters |
| |
| private static final Access IMPL_TOKEN = Access.getToken(); |
| |
| // share a cache with a friend in this package |
| Invokers getInvokers() { return invokers; } |
| void setInvokers(Invokers inv) { invokers = inv; } |
| |
| static { |
| // This hack allows the implementation package special access to |
| // the internals of MethodType. In particular, the Form has all sorts |
| // of cached information useful to the implementation code. |
| MethodTypeImpl.setMethodTypeFriend(IMPL_TOKEN, new MethodTypeImpl.MethodTypeFriend() { |
| public Class<?>[] ptypes(MethodType mt) { return mt.ptypes; } |
| public MethodTypeImpl form(MethodType mt) { return mt.form; } |
| public void setForm(MethodType mt, MethodTypeImpl form) { |
| assert(mt.form == null); |
| mt.form = (MethodTypeForm) form; |
| } |
| public MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) { |
| return MethodType.makeImpl(rtype, ptypes, trusted); |
| } |
| public MethodTypeImpl newMethodTypeForm(MethodType mt) { |
| return new MethodTypeForm(mt); |
| } |
| public Invokers getInvokers(MethodType mt) { return mt.invokers; } |
| public void setInvokers(MethodType mt, Invokers inv) { mt.invokers = inv; } |
| }); |
| } |
| |
| private MethodType(Class<?> rtype, Class<?>[] ptypes) { |
| checkRtype(rtype); |
| checkPtypes(ptypes); |
| this.rtype = rtype; |
| this.ptypes = ptypes; |
| } |
| |
| private void checkRtype(Class<?> rtype) { |
| rtype.equals(rtype); // null check |
| } |
| private void checkPtypes(Class<?>[] ptypes) { |
| for (Class<?> ptype : ptypes) { |
| ptype.equals(ptype); // null check |
| if (ptype == void.class) |
| throw newIllegalArgumentException("void parameter: "+this); |
| } |
| } |
| |
| static final HashMap<MethodType,MethodType> internTable |
| = new HashMap<MethodType, MethodType>(); |
| |
| static final Class<?>[] NO_PTYPES = {}; |
| |
| /** Find or create an instance of the given method type. |
| * @param rtype the return type |
| * @param ptypes the parameter types |
| * @return the interned method type with the given parts |
| * @throws NullPointerException if rtype or any ptype is null |
| * @throws IllegalArgumentException if any of the ptypes is void |
| */ |
| public static |
| MethodType make(Class<?> rtype, Class<?>[] ptypes) { |
| return makeImpl(rtype, ptypes, false); |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. */ |
| public static |
| MethodType make(Class<?> rtype, List<? extends Class<?>> ptypes) { |
| return makeImpl(rtype, ptypes.toArray(NO_PTYPES), true); |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. |
| * The leading parameter type is prepended to the remaining array. |
| */ |
| public static |
| MethodType make(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) { |
| Class<?>[] ptypes1 = new Class<?>[1+ptypes.length]; |
| ptypes1[0] = ptype0; |
| System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length); |
| return makeImpl(rtype, ptypes1, true); |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. |
| * The resulting method has no parameter types. |
| */ |
| public static |
| MethodType make(Class<?> rtype) { |
| return makeImpl(rtype, NO_PTYPES, true); |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. |
| * The resulting method has the single given parameter type. |
| */ |
| public static |
| MethodType make(Class<?> rtype, Class<?> ptype0) { |
| return makeImpl(rtype, new Class<?>[]{ ptype0 }, true); |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. |
| * The resulting method has the same parameter types as {@code ptypes}, |
| * and the specified return type. |
| */ |
| public static |
| MethodType make(Class<?> rtype, MethodType ptypes) { |
| return makeImpl(rtype, ptypes.ptypes, true); |
| } |
| |
| /** |
| * Sole factory method to find or create an interned method type. |
| * @param rtype desired return type |
| * @param ptypes desired parameter types |
| * @param trusted whether the ptypes can be used without cloning |
| * @return the unique method type of the desired structure |
| */ |
| private static |
| MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) { |
| if (ptypes == null || ptypes.length == 0) { |
| ptypes = NO_PTYPES; trusted = true; |
| } |
| MethodType mt1 = new MethodType(rtype, ptypes); |
| MethodType mt0; |
| synchronized (internTable) { |
| mt0 = internTable.get(mt1); |
| if (mt0 != null) |
| return mt0; |
| } |
| if (!trusted) |
| // defensively copy the array passed in by the user |
| mt1 = new MethodType(rtype, ptypes.clone()); |
| // promote the object to the Real Thing, and reprobe |
| MethodTypeImpl.initForm(IMPL_TOKEN, mt1); |
| synchronized (internTable) { |
| mt0 = internTable.get(mt1); |
| if (mt0 != null) |
| return mt0; |
| internTable.put(mt1, mt1); |
| } |
| return mt1; |
| } |
| |
| // Entry point from JVM. TODO: Change the name & signature. |
| private static MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, |
| boolean ignore1, boolean ignore2) { |
| return makeImpl(rtype, ptypes, true); |
| } |
| |
| private static final MethodType[] objectOnlyTypes = new MethodType[20]; |
| |
| /** |
| * Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. |
| * All parameters and the return type will be Object, except the final varargs parameter if any. |
| * @param objectArgCount number of parameters (excluding the varargs parameter if any) |
| * @param varargs whether there will be a varargs parameter, of type Object[] |
| * @return a totally generic method type, given only its count of parameters and varargs |
| * @see #makeGeneric(int) |
| */ |
| public static |
| MethodType makeGeneric(int objectArgCount, boolean varargs) { |
| MethodType mt; |
| int ivarargs = (!varargs ? 0 : 1); |
| int ootIndex = objectArgCount*2 + ivarargs; |
| if (ootIndex < objectOnlyTypes.length) { |
| mt = objectOnlyTypes[ootIndex]; |
| if (mt != null) return mt; |
| } |
| Class<?>[] ptypes = new Class<?>[objectArgCount + ivarargs]; |
| Arrays.fill(ptypes, Object.class); |
| if (ivarargs != 0) ptypes[objectArgCount] = Object[].class; |
| mt = makeImpl(Object.class, ptypes, true); |
| if (ootIndex < objectOnlyTypes.length) { |
| objectOnlyTypes[ootIndex] = mt; // cache it here also! |
| } |
| return mt; |
| } |
| |
| /** |
| * All parameters and the return type will be Object. |
| * @param objectArgCount number of parameters |
| * @return a totally generic method type, given only its count of parameters |
| * @see #makeGeneric(int, boolean) |
| */ |
| public static |
| MethodType makeGeneric(int objectArgCount) { |
| return makeGeneric(objectArgCount, false); |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. |
| * @param num the index (zero-based) of the parameter type to change |
| * @param nptype a new parameter type to replace the old one with |
| * @return the same type, except with the selected parameter changed |
| */ |
| public MethodType changeParameterType(int num, Class<?> nptype) { |
| if (parameterType(num) == nptype) return this; |
| Class<?>[] nptypes = ptypes.clone(); |
| nptypes[num] = nptype; |
| return makeImpl(rtype, nptypes, true); |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. |
| * @param num the position (zero-based) of the inserted parameter type |
| * @param nptype a new parameter type to insert into the parameter list |
| * @return the same type, except with the selected parameter inserted |
| */ |
| public MethodType insertParameterType(int num, Class<?> nptype) { |
| int len = ptypes.length; |
| Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+1); |
| System.arraycopy(nptypes, num, nptypes, num+1, len-num); |
| nptypes[num] = nptype; |
| return makeImpl(rtype, nptypes, true); |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. |
| * @param num the index (zero-based) of the parameter type to remove |
| * @return the same type, except with the selected parameter removed |
| */ |
| public MethodType dropParameterType(int num) { |
| int len = ptypes.length; |
| Class<?>[] nptypes; |
| if (num == 0) { |
| nptypes = Arrays.copyOfRange(ptypes, 1, len); |
| } else { |
| nptypes = Arrays.copyOfRange(ptypes, 0, len-1); |
| System.arraycopy(ptypes, num+1, nptypes, num, (len-1)-num); |
| } |
| return makeImpl(rtype, nptypes, true); |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. |
| * @param nrtype a return parameter type to replace the old one with |
| * @return the same type, except with the return type change |
| */ |
| public MethodType changeReturnType(Class<?> nrtype) { |
| if (returnType() == nrtype) return this; |
| return makeImpl(nrtype, ptypes, true); |
| } |
| |
| /** Convenience method. |
| * Report if this type contains a primitive argument or return value. |
| * @return true if any of the types are primitives |
| */ |
| public boolean hasPrimitives() { |
| return form.hasPrimitives(); |
| } |
| |
| /** Convenience method. |
| * Report if this type contains a wrapper argument or return value. |
| * Wrappers are types which box primitive values, such as {@link Integer}. |
| * @return true if any of the types are wrappers |
| */ |
| public boolean hasWrappers() { |
| return unwrap() != this; |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. |
| * Erase all reference types to Object. |
| * @return a version of the original type with all reference types replaced |
| */ |
| public MethodType erase() { |
| return form.erasedType(); |
| } |
| |
| /** Convenience method for {@link #makeGeneric(int)}. |
| * Convert all types, both reference and primitive, to Object. |
| * @return a version of the original type with all types replaced |
| */ |
| public MethodType generic() { |
| return makeGeneric(parameterCount()); |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. |
| * Convert all primitive types to their corresponding wrapper types. |
| * A {@code void} return type is changed to the type {@code java.lang.Void}. |
| * @return a version of the original type with all primitive types replaced |
| */ |
| public MethodType wrap() { |
| return hasPrimitives() ? wrapWithPrims(this) : this; |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. |
| * Convert all wrapper types to their corresponding primitive types. |
| * A return type of {@java.lang.Void} is changed to {@code void}. |
| * @return a version of the original type with all wrapper types replaced |
| */ |
| public MethodType unwrap() { |
| MethodType noprims = !hasPrimitives() ? this : wrapWithPrims(this); |
| return unwrapWithNoPrims(noprims); |
| } |
| |
| private static MethodType wrapWithPrims(MethodType pt) { |
| assert(pt.hasPrimitives()); |
| MethodType wt = pt.wrapAlt; |
| if (wt == null) { |
| // fill in lazily |
| wt = MethodTypeImpl.canonicalize(pt, MethodTypeImpl.WRAP, MethodTypeImpl.WRAP); |
| assert(wt != null); |
| pt.wrapAlt = wt; |
| } |
| return wt; |
| } |
| |
| private static MethodType unwrapWithNoPrims(MethodType wt) { |
| assert(!wt.hasPrimitives()); |
| MethodType uwt = wt.wrapAlt; |
| if (uwt == null) { |
| // fill in lazily |
| uwt = MethodTypeImpl.canonicalize(wt, MethodTypeImpl.UNWRAP, MethodTypeImpl.UNWRAP); |
| if (uwt == null) |
| uwt = wt; // type has no wrappers or prims at all |
| wt.wrapAlt = uwt; |
| } |
| return uwt; |
| } |
| |
| /** @param num the index (zero-based) of the desired parameter type |
| * @return the selected parameter type |
| */ |
| public Class<?> parameterType(int num) { |
| return ptypes[num]; |
| } |
| /** @return the number of parameter types */ |
| public int parameterCount() { |
| return ptypes.length; |
| } |
| /** @return the return type */ |
| public Class<?> returnType() { |
| return rtype; |
| } |
| |
| /** |
| * Convenience method to present the arguments as a list. |
| * @return the parameter types (as an immutable list) |
| */ |
| public List<Class<?>> parameterList() { |
| return Collections.unmodifiableList(Arrays.asList(ptypes)); |
| } |
| |
| /** |
| * Convenience method to present the arguments as an array. |
| * @return the parameter types (as a fresh copy if necessary) |
| */ |
| public Class<?>[] parameterArray() { |
| return ptypes.clone(); |
| } |
| |
| /** |
| * Compares the specified object with this type for equality. |
| * That is, it returns <tt>true</tt> if and only if the specified object |
| * is also a method type with exactly the same parameters and return type. |
| * @param x object to compare |
| * @see Object#equals(Object) |
| */ |
| @Override |
| public boolean equals(Object x) { |
| return this == x || x instanceof MethodType && equals((MethodType)x); |
| } |
| |
| private boolean equals(MethodType that) { |
| return this.rtype == that.rtype |
| && Arrays.equals(this.ptypes, that.ptypes); |
| } |
| |
| /** |
| * Returns the hash code value for this method type. |
| * It is defined to be the same as the hashcode of a List |
| * whose elements are the return type followed by the |
| * parameter types. |
| * @return the hash code value for this method type |
| * @see Object#hashCode() |
| * @see #equals(Object) |
| * @see List#hashCode() |
| */ |
| @Override |
| public int hashCode() { |
| int hashCode = 31 + rtype.hashCode(); |
| for (Class<?> ptype : ptypes) |
| hashCode = 31*hashCode + ptype.hashCode(); |
| return hashCode; |
| } |
| |
| /** |
| * The string representation of a method type is a |
| * parenthesis enclosed, comma separated list of type names, |
| * followed immediately by the return type. |
| * <p> |
| * If a type name is array, it the base type followed |
| * by [], rather than the Class.getName of the array type. |
| */ |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("("); |
| for (int i = 0; i < ptypes.length; i++) { |
| if (i > 0) sb.append(","); |
| putName(sb, ptypes[i]); |
| } |
| sb.append(")"); |
| putName(sb, rtype); |
| return sb.toString(); |
| } |
| |
| static void putName(StringBuilder sb, Class<?> cls) { |
| int brackets = 0; |
| while (cls.isArray()) { |
| cls = cls.getComponentType(); |
| brackets++; |
| } |
| String n = cls.getName(); |
| /* |
| if (n.startsWith("java.lang.")) { |
| String nb = n.substring("java.lang.".length()); |
| if (nb.indexOf('.') < 0) n = nb; |
| } else if (n.indexOf('.') < 0) { |
| n = "."+n; // anonymous package |
| } |
| */ |
| sb.append(n); |
| while (brackets > 0) { |
| sb.append("[]"); |
| brackets--; |
| } |
| } |
| |
| /// Queries which have to do with the bytecode architecture |
| |
| /** The number of JVM stack slots required to invoke a method |
| * of this type. Note that (for historic reasons) the JVM requires |
| * a second stack slot to pass long and double arguments. |
| * So this method returns {@link #parameterCount()} plus the |
| * number of long and double parameters (if any). |
| * <p> |
| * This method is included for the benfit of applications that must |
| * generate bytecodes that process method handles and invokedynamic. |
| * @return the number of JVM stack slots for this type's parameters |
| */ |
| public int parameterSlotCount() { |
| return form.parameterSlotCount(); |
| } |
| |
| /** Number of JVM stack slots which carry all parameters after |
| * the given position, which must be in the range of 0 to |
| * {@code parameterCount} inclusive. Successive parameters are |
| * more shallowly stacked, and parameters are indexed in the bytecodes |
| * according to their trailing edge. Thus, to obtain the depth |
| * in the outgoing call stack of parameter {@code N}, obtain |
| * the {@code parameterSlotDepth} of its trailing edge |
| * at position {@code N+1}. |
| * <p> |
| * Parameters of type {@code long} and {@code double} occupy |
| * two stack slots (for historical reasons) and all others occupy one. |
| * Therefore, the number returned is the number of arguments |
| * <em>including</em> and <em>after</em> the given parameter, |
| * <em>plus</em> the number of long or double arguments |
| * at or after after the argument for the given parameter. |
| * <p> |
| * This method is included for the benfit of applications that must |
| * generate bytecodes that process method handles and invokedynamic. |
| * @param num an index (zero-based, inclusive) within the parameter types |
| * @return the index of the (shallowest) JVM stack slot transmitting the |
| * given parameter |
| */ |
| public int parameterSlotDepth(int num) { |
| if (num < 0 || num > ptypes.length) |
| parameterType(num); // force a range check |
| return form.parameterToArgSlot(num-1); |
| } |
| |
| /** The number of JVM stack slots required to receive a return value |
| * from a method of this type. |
| * If the {@link #returnType() return type} is void, it will be zero, |
| * else if the return type is long or double, it will be two, else one. |
| * <p> |
| * This method is included for the benfit of applications that must |
| * generate bytecodes that process method handles and invokedynamic. |
| * @return the number of JVM stack slots (0, 1, or 2) for this type's return value |
| */ |
| public int returnSlotCount() { |
| return form.returnSlotCount(); |
| } |
| |
| /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. |
| * Find or create an instance (interned) of the given method type. |
| * Any class or interface name embedded in the signature string |
| * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)} |
| * on the given loader (or if it is null, on the system class loader). |
| * <p> |
| * Note that it is possible to build method types which cannot be |
| * constructed by this method, because their component types are |
| * not all reachable from a common class loader. |
| * <p> |
| * This method is included for the benfit of applications that must |
| * generate bytecodes that process method handles and invokedynamic. |
| * @param bytecodeSignature a bytecode-level signature string "(T...)T" |
| * @param loader the class loader in which to look up the types |
| * @return a method type matching the bytecode-level signature |
| * @throws IllegalArgumentException if the string is not well-formed |
| * @throws TypeNotPresentException if a named type cannot be found |
| */ |
| public static MethodType fromBytecodeString(String bytecodeSignature, ClassLoader loader) |
| throws IllegalArgumentException, TypeNotPresentException |
| { |
| List<Class<?>> types = BytecodeSignature.parseMethod(bytecodeSignature, loader); |
| Class<?> rtype = types.remove(types.size() - 1); |
| Class<?>[] ptypes = types.toArray(NO_PTYPES); |
| return makeImpl(rtype, ptypes, true); |
| } |
| |
| /** |
| * Create a bytecode signature representation of the type. |
| * Note that this is not a strict inverse of |
| * <p> |
| * This method is included for the benfit of applications that must |
| * generate bytecodes that process method handles and invokedynamic. |
| * {@link #fromBytecodeString(java.lang.String, java.lang.ClassLoader)}, |
| * because the latter requires a suitable class loader argument. |
| * @return the bytecode signature representation |
| */ |
| public String toBytecodeString() { |
| return BytecodeSignature.unparse(this); |
| } |
| } |