src/share/classes/sun/dyn/MethodTypeImpl.java
author jrose
Tue May 05 22:40:09 2009 -0700 (6 months ago)
changeset 1184 d201987cb76c
permissions -rw-r--r--
6829144: JSR 292 JVM features need a provisional Java API
Summary: JDK API and runtime (partial) for anonk, meth, indy
Reviewed-by: mr
        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 sun.dyn;
       27 
       28 import java.dyn.*;
       29 import sun.dyn.util.Wrapper;
       30 
       31 /**
       32  * Shared information for a group of method types, which differ
       33  * only by reference types, and therefore share a common erasure
       34  * and wrapping.
       35  * <p>
       36  * For an empirical discussion of the structure of method types,
       37  * see <a href="http://groups.google.com/group/jvm-languages/browse_thread/thread/ac9308ae74da9b7e/">
       38  * the thread "Avoiding Boxing" on jvm-languages</a>.
       39  * There are approximately 2000 distinct erased method types in the JDK.
       40  * There are a little over 10 times that number of unerased types.
       41  * No more than half of these are likely to be loaded at once.
       42  * @author John Rose
       43  */
       44 public class MethodTypeImpl {
       45     final int[] argToSlotTable, slotToArgTable;
       46     final long argCounts;               // packed slot & value counts
       47     final long primCounts;              // packed prim & double counts
       48     final int vmslots;                  // total number of parameter slots
       49     final MethodType erasedType;        // the canonical erasure
       50     /*lazy*/ MethodType primsAsBoxes;   // replace prims by wrappers
       51     /*lazy*/ MethodType primArgsAsBoxes; // wrap args only; make raw return
       52     /*lazy*/ MethodType primsAsInts;    // replace prims by int/long
       53     /*lazy*/ MethodType primsAsLongs;   // replace prims by long
       54     /*lazy*/ MethodType primsAtEnd;     // reorder primitives to the end
       55 
       56     // Cached adapter information:
       57     /*lazy*/ ToGeneric   toGeneric;     // convert cs. with prims to w/o
       58     /*lazy*/ FromGeneric fromGeneric;   // convert cs. w/o prims to with
       59     /*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly
       60     ///*lazy*/ Invokers    invokers;    // cache of handy higher-order adapters
       61 
       62     public MethodType erasedType() {
       63         return erasedType;
       64     }
       65 
       66     public static MethodTypeImpl of(MethodType type) {
       67         return METHOD_TYPE_FRIEND.form(type);
       68     }
       69 
       70     /** Access methods for the internals of MethodType, supplied to
       71      *  MethodTypeForm as a trusted agent.
       72      */
       73     static public interface MethodTypeFriend {
       74         Class<?>[]     ptypes(MethodType mt);
       75         MethodTypeImpl form(MethodType mt);
       76         void           setForm(MethodType mt, MethodTypeImpl form);
       77         MethodType     makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted);
       78         MethodTypeImpl newMethodTypeForm(MethodType mt);
       79         Invokers       getInvokers(MethodType mt);
       80         void           setInvokers(MethodType mt, Invokers inv);
       81     }
       82     public static void setMethodTypeFriend(Access token, MethodTypeFriend am) {
       83         Access.check(token);
       84         if (METHOD_TYPE_FRIEND != null)
       85             throw new InternalError();  // just once
       86         METHOD_TYPE_FRIEND = am;
       87     }
       88     static private MethodTypeFriend METHOD_TYPE_FRIEND;
       89 
       90     protected MethodTypeImpl(MethodType erasedType) {
       91         this.erasedType = erasedType;
       92 
       93         Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(erasedType);
       94         int ptypeCount = ptypes.length;
       95         int pslotCount = ptypeCount;            // temp. estimate
       96         int rtypeCount = 1;                     // temp. estimate
       97         int rslotCount = 1;                     // temp. estimate
       98 
       99         int[] argToSlotTab = null, slotToArgTab = null;
      100 
      101         // Walk the argument types, looking for primitives.
      102         int pac = 0, lac = 0, prc = 0, lrc = 0;
      103         Class<?> epts[] = ptypes;
      104         for (int i = 0; i < epts.length; i++) {
      105             Class<?> pt = epts[i];
      106             if (pt != Object.class) {
      107                 assert(pt.isPrimitive());
      108                 ++pac;
      109                 if (hasTwoArgSlots(pt))  ++lac;
      110             }
      111         }
      112         pslotCount += lac;                  // #slots = #args + #longs
      113         Class<?> rt = erasedType.returnType();
      114         if (rt != Object.class) {
      115             ++prc;          // even void.class counts as a prim here
      116             if (hasTwoArgSlots(rt))  ++lrc;
      117             // adjust #slots, #args
      118             if (rt == void.class)
      119                 rtypeCount = rslotCount = 0;
      120             else
      121                 rslotCount += lrc;
      122         }
      123         if (lac != 0) {
      124             int slot = ptypeCount + lac;
      125             slotToArgTab = new int[slot+1];
      126             argToSlotTab = new int[1+ptypeCount];
      127             argToSlotTab[0] = slot;  // argument "-1" is past end of slots
      128             for (int i = 0; i < epts.length; i++) {
      129                 Class<?> pt = epts[i];
      130                 if (hasTwoArgSlots(pt))  --slot;
      131                 --slot;
      132                 slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
      133                 argToSlotTab[1+i]  = slot;
      134             }
      135             assert(slot == 0);  // filled the table
      136         }
      137         this.primCounts = pack(lrc, prc, lac, pac);
      138         this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
      139         if (slotToArgTab == null) {
      140             int slot = ptypeCount; // first arg is deepest in stack
      141             slotToArgTab = new int[slot+1];
      142             argToSlotTab = new int[1+ptypeCount];
      143             argToSlotTab[0] = slot;  // argument "-1" is past end of slots
      144             for (int i = 0; i < ptypeCount; i++) {
      145                 --slot;
      146                 slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
      147                 argToSlotTab[1+i]  = slot;
      148             }
      149         }
      150         this.argToSlotTable = argToSlotTab;
      151         this.slotToArgTable = slotToArgTab;
      152 
      153         if (pslotCount >= 256)  throw new IllegalArgumentException("too many arguments");
      154 
      155         // send a few bits down to the JVM:
      156         this.vmslots = parameterSlotCount();
      157 
      158         // short circuit some no-op canonicalizations:
      159         if (!hasPrimitives()) {
      160             primsAsBoxes = erasedType;
      161             primArgsAsBoxes = erasedType;
      162             primsAsInts  = erasedType;
      163             primsAsLongs = erasedType;
      164             primsAtEnd   = erasedType;
      165         }
      166     }
      167 
      168     /** Turn all primitive types to corresponding wrapper types.
      169      */
      170     public MethodType primsAsBoxes() {
      171         MethodType ct = primsAsBoxes;
      172         if (ct != null)  return ct;
      173         MethodType t = erasedType;
      174         ct = canonicalize(erasedType, WRAP, WRAP);
      175         if (ct == null)  ct = t;  // no prims to box
      176         return primsAsBoxes = ct;
      177     }
      178 
      179     /** Turn all primitive argument types to corresponding wrapper types.
      180      *  Subword and void return types are promoted to int.
      181      */
      182     public MethodType primArgsAsBoxes() {
      183         MethodType ct = primArgsAsBoxes;
      184         if (ct != null)  return ct;
      185         MethodType t = erasedType;
      186         ct = canonicalize(erasedType, RAW_RETURN, WRAP);
      187         if (ct == null)  ct = t;  // no prims to box
      188         return primArgsAsBoxes = ct;
      189     }
      190 
      191     /** Turn all primitive types to either int or long.
      192      *  Floating point return types are not changed, because
      193      *  they may require special calling sequences.
      194      *  A void return value is turned to int.
      195      */
      196     public MethodType primsAsInts() {
      197         MethodType ct = primsAsInts;
      198         if (ct != null)  return ct;
      199         MethodType t = erasedType;
      200         ct = canonicalize(t, RAW_RETURN, INTS);
      201         if (ct == null)  ct = t;  // no prims to int-ify
      202         return primsAsInts = ct;
      203     }
      204 
      205     /** Turn all primitive types to either int or long.
      206      *  Floating point return types are not changed, because
      207      *  they may require special calling sequences.
      208      *  A void return value is turned to int.
      209      */
      210     public MethodType primsAsLongs() {
      211         MethodType ct = primsAsLongs;
      212         if (ct != null)  return ct;
      213         MethodType t = erasedType;
      214         ct = canonicalize(t, RAW_RETURN, LONGS);
      215         if (ct == null)  ct = t;  // no prims to int-ify
      216         return primsAsLongs = ct;
      217     }
      218 
      219     /** Stably sort parameters into 3 buckets: ref, int, long. */
      220     public MethodType primsAtEnd() {
      221         MethodType ct = primsAtEnd;
      222         if (ct != null)  return ct;
      223         MethodType t = erasedType;
      224 
      225         int pac = primitiveParameterCount();
      226         if (pac == 0)
      227             return primsAtEnd = t;
      228 
      229         int argc = parameterCount();
      230         int lac = longPrimitiveParameterCount();
      231         if (pac == argc && (lac == 0 || lac == argc))
      232             return primsAtEnd = t;
      233 
      234         // known to have a mix of 2 or 3 of ref, int, long
      235         return primsAtEnd = reorderParameters(t, primsAtEndOrder(t), null);
      236 
      237     }
      238 
      239     /** Compute a new ordering of parameters so that all references
      240      *  are before all ints or longs, and all ints are before all longs.
      241      *  For this ordering, doubles count as longs, and all other primitive
      242      *  values count as ints.
      243      *  As a special case, if the parameters are already in the specified
      244      *  order, this method returns a null reference, rather than an array
      245      *  specifying a null permutation.
      246      *  <p>
      247      *  For example, the type {@code (int,boolean,int,Object,String)void}
      248      *  produces the order {@code {3,4,0,1,2}}, the type
      249      *  {@code (long,int,String)void} produces {@code {2,1,2}}, and
      250      *  the type {@code (Object,int)Object} produces {@code null}.
      251      */
      252     public static int[] primsAtEndOrder(MethodType mt) {
      253         MethodTypeImpl form = METHOD_TYPE_FRIEND.form(mt);
      254         if (form.primsAtEnd == form.erasedType)
      255             // quick check shows no reordering is necessary
      256             return null;
      257 
      258         int argc = form.parameterCount();
      259         int[] paramOrder = new int[argc];
      260 
      261         // 3-way bucket sort:
      262         int pac = form.primitiveParameterCount();
      263         int lac = form.longPrimitiveParameterCount();
      264         int rfill = 0, ifill = argc - pac, lfill = argc - lac;
      265 
      266         Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt);
      267         boolean changed = false;
      268         for (int i = 0; i < ptypes.length; i++) {
      269             Class<?> pt = ptypes[i];
      270             int ord;
      271             if (!pt.isPrimitive())             ord = rfill++;
      272             else if (!hasTwoArgSlots(pt))      ord = ifill++;
      273             else                               ord = lfill++;
      274             if (ord != i)  changed = true;
      275             paramOrder[i] = ord;
      276         }
      277         assert(rfill == argc - pac && ifill == argc - lac && lfill == argc);
      278         if (!changed) {
      279             form.primsAtEnd = form.erasedType;
      280             return null;
      281         }
      282         return paramOrder;
      283     }
      284 
      285     /** Put the existing parameters of mt into a new order, given by newParamOrder.
      286      *  The third argument is logically appended to mt.parameterArray,
      287      *  so that elements of newParamOrder can index either pre-existing or
      288      *  new parameter types.
      289      */
      290     public static MethodType reorderParameters(MethodType mt, int[] newParamOrder, Class<?>[] moreParams) {
      291         if (newParamOrder == null)  return mt;  // no-op reordering
      292         Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt);
      293         Class<?>[] ntypes = new Class<?>[newParamOrder.length];
      294         int ordMax = ptypes.length + (moreParams == null ? 0 : moreParams.length);
      295         boolean changed = (ntypes.length != ptypes.length);
      296         for (int i = 0; i < newParamOrder.length; i++) {
      297             int ord = newParamOrder[i];
      298             if (ord != i)  changed = true;
      299             Class<?> nt;
      300             if (ord < ptypes.length)   nt = ptypes[ord];
      301             else if (ord == ordMax)    nt = mt.returnType();
      302             else                       nt = moreParams[ord - ptypes.length];
      303             ntypes[i] = nt;
      304         }
      305         if (!changed)  return mt;
      306         return METHOD_TYPE_FRIEND.makeImpl(mt.returnType(), ntypes, true);
      307     }
      308 
      309     private static boolean hasTwoArgSlots(Class<?> type) {
      310         return type == long.class || type == double.class;
      311     }
      312 
      313     private static long pack(int a, int b, int c, int d) {
      314         assert(((a|b|c|d) & ~0xFFFF) == 0);
      315         long hw = ((a << 16) | b), lw = ((c << 16) | d);
      316         return (hw << 32) | lw;
      317     }
      318     private static char unpack(long packed, int word) { // word==0 => return a, ==3 => return d
      319         assert(word <= 3);
      320         return (char)(packed >> ((3-word) * 16));
      321     }
      322 
      323     public int parameterCount() {                      // # outgoing values
      324         return unpack(argCounts, 3);
      325     }
      326     public int parameterSlotCount() {                  // # outgoing interpreter slots
      327         return unpack(argCounts, 2);
      328     }
      329     public int returnCount() {                         // = 0 (V), or 1
      330         return unpack(argCounts, 1);
      331     }
      332     public int returnSlotCount() {                     // = 0 (V), 2 (J/D), or 1
      333         return unpack(argCounts, 0);
      334     }
      335     public int primitiveParameterCount() {
      336         return unpack(primCounts, 3);
      337     }
      338     public int longPrimitiveParameterCount() {
      339         return unpack(primCounts, 2);
      340     }
      341     public int primitiveReturnCount() {                // = 0 (obj), or 1
      342         return unpack(primCounts, 1);
      343     }
      344     public int longPrimitiveReturnCount() {            // = 1 (J/D), or 0
      345         return unpack(primCounts, 0);
      346     }
      347     public boolean hasPrimitives() {
      348         return primCounts != 0;
      349     }
      350 //    public boolean hasNonVoidPrimitives() {
      351 //        if (primCounts == 0)  return false;
      352 //        if (primitiveParameterCount() != 0)  return true;
      353 //        return (primitiveReturnCount() != 0 && returnCount() != 0);
      354 //    }
      355     public boolean hasLongPrimitives() {
      356         return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0;
      357     }
      358     public int parameterToArgSlot(int i) {
      359         return argToSlotTable[1+i];
      360     }
      361     public int argSlotToParameter(int argSlot) {
      362         // Note:  Empty slots are represented by zero in this table.
      363         // Valid arguments slots contain incremented entries, so as to be non-zero.
      364         // We return -1 the caller to mean an empty slot.
      365         return slotToArgTable[argSlot] - 1;
      366     }
      367 
      368     public static void initForm(Access token, MethodType mt) {
      369         Access.check(token);
      370         MethodTypeImpl form = findForm(mt);
      371         METHOD_TYPE_FRIEND.setForm(mt, form);
      372         if (form.erasedType == mt) {
      373             // This is a principal (erased) type; show it to the JVM.
      374             MethodHandleImpl.init(token, mt);
      375         }
      376     }
      377 
      378     static MethodTypeImpl findForm(MethodType mt) {
      379         MethodType erased = canonicalize(mt, ERASE, ERASE);
      380         if (erased == null) {
      381             // It is already erased.  Make a new MethodTypeForm.
      382             return METHOD_TYPE_FRIEND.newMethodTypeForm(mt);
      383         } else {
      384             // Share the MethodTypeForm with the erased version.
      385             return METHOD_TYPE_FRIEND.form(erased);
      386         }
      387     }
      388 
      389     /** Codes for {@link #canonicalize(java.lang.Class, int).
      390      * ERASE means change every reference to {@code Object}.
      391      * WRAP means convert primitives (including {@code void} to their
      392      * corresponding wrapper types.  UNWRAP means the reverse of WRAP.
      393      * INTS means convert all non-void primitive types to int or long,
      394      * according to size.  LONGS means convert all non-void primitives
      395      * to long, regardless of size.  RAW_RETURN means convert a type
      396      * (assumed to be a return type) to int if it is smaller than an int,
      397      * or if it is void.
      398      */
      399     public static final int NO_CHANGE = 0, ERASE = 1, WRAP = 2, UNWRAP = 3, INTS = 4, LONGS = 5, RAW_RETURN = 6;
      400 
      401     /** Canonicalize the types in the given method type.
      402      * If any types change, intern the new type, and return it.
      403      * Otherwise return null.
      404      */
      405     public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) {
      406         Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt);
      407         Class<?>[] ptc = MethodTypeImpl.canonicalizes(ptypes, howArgs);
      408         Class<?> rtype = mt.returnType();
      409         Class<?> rtc = MethodTypeImpl.canonicalize(rtype, howRet);
      410         if (ptc == null && rtc == null) {
      411             // It is already canonical.
      412             return null;
      413         }
      414         // Find the erased version of the method type:
      415         if (rtc == null)  rtc = rtype;
      416         if (ptc == null)  ptc = ptypes;
      417         return METHOD_TYPE_FRIEND.makeImpl(rtc, ptc, true);
      418     }
      419 
      420     /** Canonicalize the given return or param type.
      421      *  Return null if the type is already canonicalized.
      422      */
      423     static Class<?> canonicalize(Class<?> t, int how) {
      424         Class<?> ct;
      425         if (t == Object.class) {
      426             // no change, ever
      427         } else if (!t.isPrimitive()) {
      428             switch (how) {
      429                 case UNWRAP:
      430                     ct = Wrapper.asPrimitiveType(t);
      431                     if (ct != t)  return ct;
      432                     break;
      433                 case RAW_RETURN:
      434                 case ERASE:
      435                     return Object.class;
      436             }
      437         } else if (t == void.class) {
      438             // no change, usually
      439             switch (how) {
      440                 case RAW_RETURN:
      441                     return int.class;
      442                 case WRAP:
      443                     return Void.class;
      444             }
      445         } else {
      446             // non-void primitive
      447             switch (how) {
      448                 case WRAP:
      449                     return Wrapper.asWrapperType(t);
      450                 case INTS:
      451                     if (t == int.class || t == long.class)
      452                         return null;  // no change
      453                     if (t == double.class)
      454                         return long.class;
      455                     return int.class;
      456                 case LONGS:
      457                     if (t == long.class)
      458                         return null;  // no change
      459                     return long.class;
      460                 case RAW_RETURN:
      461                     if (t == int.class || t == long.class ||
      462                         t == float.class || t == double.class)
      463                         return null;  // no change
      464                     // everything else returns as an int
      465                     return int.class;
      466             }
      467         }
      468         // no change; return null to signify
      469         return null;
      470     }
      471 
      472     /** Canonicalize each param type in the given array.
      473      *  Return null if all types are already canonicalized.
      474      */
      475     static Class<?>[] canonicalizes(Class<?>[] ts, int how) {
      476         Class<?>[] cs = null;
      477         for (int imax = ts.length, i = 0; i < imax; i++) {
      478             Class<?> c = canonicalize(ts[i], how);
      479             if (c != null) {
      480                 if (cs == null)
      481                     cs = ts.clone();
      482                 cs[i] = c;
      483             }
      484         }
      485         return cs;
      486     }
      487 
      488     public static Invokers invokers(Access token, MethodType type) {
      489         Access.check(token);
      490         Invokers inv = METHOD_TYPE_FRIEND.getInvokers(type);
      491         if (inv != null)  return inv;
      492         inv = new Invokers(token, type);
      493         METHOD_TYPE_FRIEND.setInvokers(type, inv);
      494         return inv;
      495     }
      496 
      497     @Override
      498     public String toString() {
      499         return "Form"+erasedType;
      500     }
      501 
      502 }