Home » openjdk-7 » java.lang.invoke » [javadoc | source]

    1   /*
    2    * Copyright (c) 2008, 2011, Oracle and/or its affiliates. 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.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package java.lang.invoke;
   27   
   28   import sun.invoke.util.Wrapper;
   29   import static java.lang.invoke.MethodHandleStatics.*;
   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   class MethodTypeForm {
   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       private Object vmlayout;            // vm-specific information for calls
   50       final MethodType erasedType;        // the canonical erasure
   51   
   52       /*lazy*/ MethodType primsAsBoxes;   // replace prims by wrappers
   53       /*lazy*/ MethodType primArgsAsBoxes; // wrap args only; make raw return
   54       /*lazy*/ MethodType primsAsInts;    // replace prims by int/long
   55       /*lazy*/ MethodType primsAsLongs;   // replace prims by long
   56       /*lazy*/ MethodType primsAtEnd;     // reorder primitives to the end
   57   
   58       // Cached adapter information:
   59       /*lazy*/ ToGeneric   toGeneric;     // convert cs. with prims to w/o
   60       /*lazy*/ FromGeneric fromGeneric;   // convert cs. w/o prims to with
   61       /*lazy*/ SpreadGeneric[] spreadGeneric; // expand one argument to many
   62       /*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly
   63       /*lazy*/ MethodHandle genericInvoker; // hook for inexact invoke
   64   
   65       public MethodType erasedType() {
   66           return erasedType;
   67       }
   68   
   69       protected MethodTypeForm(MethodType erasedType) {
   70           this.erasedType = erasedType;
   71   
   72           Class<?>[] ptypes = erasedType.ptypes();
   73           int ptypeCount = ptypes.length;
   74           int pslotCount = ptypeCount;            // temp. estimate
   75           int rtypeCount = 1;                     // temp. estimate
   76           int rslotCount = 1;                     // temp. estimate
   77   
   78           int[] argToSlotTab = null, slotToArgTab = null;
   79   
   80           // Walk the argument types, looking for primitives.
   81           int pac = 0, lac = 0, prc = 0, lrc = 0;
   82           Class<?> epts[] = ptypes;
   83           for (int i = 0; i < epts.length; i++) {
   84               Class<?> pt = epts[i];
   85               if (pt != Object.class) {
   86                   assert(pt.isPrimitive());
   87                   ++pac;
   88                   if (hasTwoArgSlots(pt))  ++lac;
   89               }
   90           }
   91           pslotCount += lac;                  // #slots = #args + #longs
   92           Class<?> rt = erasedType.returnType();
   93           if (rt != Object.class) {
   94               ++prc;          // even void.class counts as a prim here
   95               if (hasTwoArgSlots(rt))  ++lrc;
   96               // adjust #slots, #args
   97               if (rt == void.class)
   98                   rtypeCount = rslotCount = 0;
   99               else
  100                   rslotCount += lrc;
  101           }
  102           if (lac != 0) {
  103               int slot = ptypeCount + lac;
  104               slotToArgTab = new int[slot+1];
  105               argToSlotTab = new int[1+ptypeCount];
  106               argToSlotTab[0] = slot;  // argument "-1" is past end of slots
  107               for (int i = 0; i < epts.length; i++) {
  108                   Class<?> pt = epts[i];
  109                   if (hasTwoArgSlots(pt))  --slot;
  110                   --slot;
  111                   slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
  112                   argToSlotTab[1+i]  = slot;
  113               }
  114               assert(slot == 0);  // filled the table
  115           }
  116           this.primCounts = pack(lrc, prc, lac, pac);
  117           this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
  118           if (slotToArgTab == null) {
  119               int slot = ptypeCount; // first arg is deepest in stack
  120               slotToArgTab = new int[slot+1];
  121               argToSlotTab = new int[1+ptypeCount];
  122               argToSlotTab[0] = slot;  // argument "-1" is past end of slots
  123               for (int i = 0; i < ptypeCount; i++) {
  124                   --slot;
  125                   slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
  126                   argToSlotTab[1+i]  = slot;
  127               }
  128           }
  129           this.argToSlotTable = argToSlotTab;
  130           this.slotToArgTable = slotToArgTab;
  131   
  132           if (pslotCount >= 256)  throw newIllegalArgumentException("too many arguments");
  133   
  134           // send a few bits down to the JVM:
  135           this.vmslots = parameterSlotCount();
  136   
  137           // short circuit some no-op canonicalizations:
  138           if (!hasPrimitives()) {
  139               primsAsBoxes = erasedType;
  140               primArgsAsBoxes = erasedType;
  141               primsAsInts  = erasedType;
  142               primsAsLongs = erasedType;
  143               primsAtEnd   = erasedType;
  144           }
  145       }
  146   
  147       /** Turn all primitive types to corresponding wrapper types.
  148        */
  149       public MethodType primsAsBoxes() {
  150           MethodType ct = primsAsBoxes;
  151           if (ct != null)  return ct;
  152           MethodType t = erasedType;
  153           ct = canonicalize(erasedType, WRAP, WRAP);
  154           if (ct == null)  ct = t;  // no prims to box
  155           return primsAsBoxes = ct;
  156       }
  157   
  158       /** Turn all primitive argument types to corresponding wrapper types.
  159        *  Subword and void return types are promoted to int.
  160        */
  161       public MethodType primArgsAsBoxes() {
  162           MethodType ct = primArgsAsBoxes;
  163           if (ct != null)  return ct;
  164           MethodType t = erasedType;
  165           ct = canonicalize(erasedType, RAW_RETURN, WRAP);
  166           if (ct == null)  ct = t;  // no prims to box
  167           return primArgsAsBoxes = ct;
  168       }
  169   
  170       /** Turn all primitive types to either int or long.
  171        *  Floating point return types are not changed, because
  172        *  they may require special calling sequences.
  173        *  A void return value is turned to int.
  174        */
  175       public MethodType primsAsInts() {
  176           MethodType ct = primsAsInts;
  177           if (ct != null)  return ct;
  178           MethodType t = erasedType;
  179           ct = canonicalize(t, RAW_RETURN, INTS);
  180           if (ct == null)  ct = t;  // no prims to int-ify
  181           return primsAsInts = ct;
  182       }
  183   
  184       /** Turn all primitive types to either int or long.
  185        *  Floating point return types are not changed, because
  186        *  they may require special calling sequences.
  187        *  A void return value is turned to int.
  188        */
  189       public MethodType primsAsLongs() {
  190           MethodType ct = primsAsLongs;
  191           if (ct != null)  return ct;
  192           MethodType t = erasedType;
  193           ct = canonicalize(t, RAW_RETURN, LONGS);
  194           if (ct == null)  ct = t;  // no prims to int-ify
  195           return primsAsLongs = ct;
  196       }
  197   
  198       /** Stably sort parameters into 3 buckets: ref, int, long. */
  199       public MethodType primsAtEnd() {
  200           MethodType ct = primsAtEnd;
  201           if (ct != null)  return ct;
  202           MethodType t = erasedType;
  203   
  204           int pac = primitiveParameterCount();
  205           if (pac == 0)
  206               return primsAtEnd = t;
  207   
  208           int argc = parameterCount();
  209           int lac = longPrimitiveParameterCount();
  210           if (pac == argc && (lac == 0 || lac == argc))
  211               return primsAtEnd = t;
  212   
  213           // known to have a mix of 2 or 3 of ref, int, long
  214           int[] reorder = primsAtEndOrder(t);
  215           ct = reorderParameters(t, reorder, null);
  216           //System.out.println("t="+t+" / reorder="+java.util.Arrays.toString(reorder)+" => "+ct);
  217           return primsAtEnd = ct;
  218       }
  219   
  220       /** Compute a new ordering of parameters so that all references
  221        *  are before all ints or longs, and all ints are before all longs.
  222        *  For this ordering, doubles count as longs, and all other primitive
  223        *  values count as ints.
  224        *  As a special case, if the parameters are already in the specified
  225        *  order, this method returns a null reference, rather than an array
  226        *  specifying a null permutation.
  227        *  <p>
  228        *  For example, the type {@code (int,boolean,int,Object,String)void}
  229        *  produces the order {@code {3,4,0,1,2}}, the type
  230        *  {@code (long,int,String)void} produces {@code {2,1,2}}, and
  231        *  the type {@code (Object,int)Object} produces {@code null}.
  232        */
  233       public static int[] primsAtEndOrder(MethodType mt) {
  234           MethodTypeForm form = mt.form();
  235           if (form.primsAtEnd == form.erasedType)
  236               // quick check shows no reordering is necessary
  237               return null;
  238   
  239           int argc = form.parameterCount();
  240           int[] paramOrder = new int[argc];
  241   
  242           // 3-way bucket sort:
  243           int pac = form.primitiveParameterCount();
  244           int lac = form.longPrimitiveParameterCount();
  245           int rfill = 0, ifill = argc - pac, lfill = argc - lac;
  246   
  247           Class<?>[] ptypes = mt.ptypes();
  248           boolean changed = false;
  249           for (int i = 0; i < ptypes.length; i++) {
  250               Class<?> pt = ptypes[i];
  251               int ord;
  252               if (!pt.isPrimitive())             ord = rfill++;
  253               else if (!hasTwoArgSlots(pt))      ord = ifill++;
  254               else                               ord = lfill++;
  255               if (ord != i)  changed = true;
  256               assert(paramOrder[ord] == 0);
  257               paramOrder[ord] = i;
  258           }
  259           assert(rfill == argc - pac && ifill == argc - lac && lfill == argc);
  260           if (!changed) {
  261               form.primsAtEnd = form.erasedType;
  262               return null;
  263           }
  264           return paramOrder;
  265       }
  266   
  267       /** Put the existing parameters of mt into a new order, given by newParamOrder.
  268        *  The third argument is logically appended to mt.parameterArray,
  269        *  so that elements of newParamOrder can index either pre-existing or
  270        *  new parameter types.
  271        */
  272       public static MethodType reorderParameters(MethodType mt, int[] newParamOrder, Class<?>[] moreParams) {
  273           if (newParamOrder == null)  return mt;  // no-op reordering
  274           Class<?>[] ptypes = mt.ptypes();
  275           Class<?>[] ntypes = new Class<?>[newParamOrder.length];
  276           int maxParam = ptypes.length + (moreParams == null ? 0 : moreParams.length);
  277           boolean changed = (ntypes.length != ptypes.length);
  278           for (int i = 0; i < newParamOrder.length; i++) {
  279               int param = newParamOrder[i];
  280               if (param != i)  changed = true;
  281               Class<?> nt;
  282               if (param < ptypes.length)   nt = ptypes[param];
  283               else if (param == maxParam)  nt = mt.returnType();
  284               else                         nt = moreParams[param - ptypes.length];
  285               ntypes[i] = nt;
  286           }
  287           if (!changed)  return mt;
  288           return MethodType.makeImpl(mt.returnType(), ntypes, true);
  289       }
  290   
  291       private static boolean hasTwoArgSlots(Class<?> type) {
  292           return type == long.class || type == double.class;
  293       }
  294   
  295       private static long pack(int a, int b, int c, int d) {
  296           assert(((a|b|c|d) & ~0xFFFF) == 0);
  297           long hw = ((a << 16) | b), lw = ((c << 16) | d);
  298           return (hw << 32) | lw;
  299       }
  300       private static char unpack(long packed, int word) { // word==0 => return a, ==3 => return d
  301           assert(word <= 3);
  302           return (char)(packed >> ((3-word) * 16));
  303       }
  304   
  305       public int parameterCount() {                      // # outgoing values
  306           return unpack(argCounts, 3);
  307       }
  308       public int parameterSlotCount() {                  // # outgoing interpreter slots
  309           return unpack(argCounts, 2);
  310       }
  311       public int returnCount() {                         // = 0 (V), or 1
  312           return unpack(argCounts, 1);
  313       }
  314       public int returnSlotCount() {                     // = 0 (V), 2 (J/D), or 1
  315           return unpack(argCounts, 0);
  316       }
  317       public int primitiveParameterCount() {
  318           return unpack(primCounts, 3);
  319       }
  320       public int longPrimitiveParameterCount() {
  321           return unpack(primCounts, 2);
  322       }
  323       public int primitiveReturnCount() {                // = 0 (obj), or 1
  324           return unpack(primCounts, 1);
  325       }
  326       public int longPrimitiveReturnCount() {            // = 1 (J/D), or 0
  327           return unpack(primCounts, 0);
  328       }
  329       public boolean hasPrimitives() {
  330           return primCounts != 0;
  331       }
  332   //    public boolean hasNonVoidPrimitives() {
  333   //        if (primCounts == 0)  return false;
  334   //        if (primitiveParameterCount() != 0)  return true;
  335   //        return (primitiveReturnCount() != 0 && returnCount() != 0);
  336   //    }
  337       public boolean hasLongPrimitives() {
  338           return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0;
  339       }
  340       public int parameterToArgSlot(int i) {
  341           return argToSlotTable[1+i];
  342       }
  343       public int argSlotToParameter(int argSlot) {
  344           // Note:  Empty slots are represented by zero in this table.
  345           // Valid arguments slots contain incremented entries, so as to be non-zero.
  346           // We return -1 the caller to mean an empty slot.
  347           return slotToArgTable[argSlot] - 1;
  348       }
  349   
  350       static MethodTypeForm findForm(MethodType mt) {
  351           MethodType erased = canonicalize(mt, ERASE, ERASE);
  352           if (erased == null) {
  353               // It is already erased.  Make a new MethodTypeForm.
  354               return new MethodTypeForm(mt);
  355           } else {
  356               // Share the MethodTypeForm with the erased version.
  357               return erased.form();
  358           }
  359       }
  360   
  361       /** Codes for {@link #canonicalize(java.lang.Class, int)}.
  362        * ERASE means change every reference to {@code Object}.
  363        * WRAP means convert primitives (including {@code void} to their
  364        * corresponding wrapper types.  UNWRAP means the reverse of WRAP.
  365        * INTS means convert all non-void primitive types to int or long,
  366        * according to size.  LONGS means convert all non-void primitives
  367        * to long, regardless of size.  RAW_RETURN means convert a type
  368        * (assumed to be a return type) to int if it is smaller than an int,
  369        * or if it is void.
  370        */
  371       public static final int NO_CHANGE = 0, ERASE = 1, WRAP = 2, UNWRAP = 3, INTS = 4, LONGS = 5, RAW_RETURN = 6;
  372   
  373       /** Canonicalize the types in the given method type.
  374        * If any types change, intern the new type, and return it.
  375        * Otherwise return null.
  376        */
  377       public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) {
  378           Class<?>[] ptypes = mt.ptypes();
  379           Class<?>[] ptc = MethodTypeForm.canonicalizes(ptypes, howArgs);
  380           Class<?> rtype = mt.returnType();
  381           Class<?> rtc = MethodTypeForm.canonicalize(rtype, howRet);
  382           if (ptc == null && rtc == null) {
  383               // It is already canonical.
  384               return null;
  385           }
  386           // Find the erased version of the method type:
  387           if (rtc == null)  rtc = rtype;
  388           if (ptc == null)  ptc = ptypes;
  389           return MethodType.makeImpl(rtc, ptc, true);
  390       }
  391   
  392       /** Canonicalize the given return or param type.
  393        *  Return null if the type is already canonicalized.
  394        */
  395       static Class<?> canonicalize(Class<?> t, int how) {
  396           Class<?> ct;
  397           if (t == Object.class) {
  398               // no change, ever
  399           } else if (!t.isPrimitive()) {
  400               switch (how) {
  401                   case UNWRAP:
  402                       ct = Wrapper.asPrimitiveType(t);
  403                       if (ct != t)  return ct;
  404                       break;
  405                   case RAW_RETURN:
  406                   case ERASE:
  407                       return Object.class;
  408               }
  409           } else if (t == void.class) {
  410               // no change, usually
  411               switch (how) {
  412                   case RAW_RETURN:
  413                       return int.class;
  414                   case WRAP:
  415                       return Void.class;
  416               }
  417           } else {
  418               // non-void primitive
  419               switch (how) {
  420                   case WRAP:
  421                       return Wrapper.asWrapperType(t);
  422                   case INTS:
  423                       if (t == int.class || t == long.class)
  424                           return null;  // no change
  425                       if (t == double.class)
  426                           return long.class;
  427                       return int.class;
  428                   case LONGS:
  429                       if (t == long.class)
  430                           return null;  // no change
  431                       return long.class;
  432                   case RAW_RETURN:
  433                       if (t == int.class || t == long.class ||
  434                           t == float.class || t == double.class)
  435                           return null;  // no change
  436                       // everything else returns as an int
  437                       return int.class;
  438               }
  439           }
  440           // no change; return null to signify
  441           return null;
  442       }
  443   
  444       /** Canonicalize each param type in the given array.
  445        *  Return null if all types are already canonicalized.
  446        */
  447       static Class<?>[] canonicalizes(Class<?>[] ts, int how) {
  448           Class<?>[] cs = null;
  449           for (int imax = ts.length, i = 0; i < imax; i++) {
  450               Class<?> c = canonicalize(ts[i], how);
  451               if (c == void.class)
  452                   c = null;  // a Void parameter was unwrapped to void; ignore
  453               if (c != null) {
  454                   if (cs == null)
  455                       cs = ts.clone();
  456                   cs[i] = c;
  457               }
  458           }
  459           return cs;
  460       }
  461   
  462       /*non-public*/ void notifyGenericMethodType() {
  463           if (genericInvoker != null)  return;
  464           try {
  465               // Trigger adapter creation.
  466               genericInvoker = InvokeGeneric.generalInvokerOf(erasedType);
  467           } catch (Exception ex) {
  468               Error err = new InternalError("Exception while resolving inexact invoke");
  469               err.initCause(ex);
  470               throw err;
  471           }
  472       }
  473   
  474       @Override
  475       public String toString() {
  476           return "Form"+erasedType;
  477       }
  478   
  479   }

Home » openjdk-7 » java.lang.invoke » [javadoc | source]