• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2008, 2013, 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 java.lang.ref.SoftReference;
30 import static java.lang.invoke.MethodHandleStatics.*;
31 
32 /**
33  * Shared information for a group of method types, which differ
34  * only by reference types, and therefore share a common erasure
35  * and wrapping.
36  * <p>
37  * For an empirical discussion of the structure of method types,
38  * see <a href="http://groups.google.com/group/jvm-languages/browse_thread/thread/ac9308ae74da9b7e/">
39  * the thread "Avoiding Boxing" on jvm-languages</a>.
40  * There are approximately 2000 distinct erased method types in the JDK.
41  * There are a little over 10 times that number of unerased types.
42  * No more than half of these are likely to be loaded at once.
43  * @author John Rose
44  */
45 final class MethodTypeForm {
46     final int[] argToSlotTable, slotToArgTable;
47     final long argCounts;               // packed slot & value counts
48     final long primCounts;              // packed prim & double counts
49     final MethodType erasedType;        // the canonical erasure
50     final MethodType basicType;         // the canonical erasure, with primitives simplified
51 
52     // Cached adapter information:
53     //
54     // @Stable final SoftReference<MethodHandle>[] methodHandles;
55     // static final int  MH_BASIC_INV, MH_NF_INV, MH_UNINIT_CS, MH_LIMIT;
56     //
57     // Cached lambda form information, for basic types only:
58     // final @Stable SoftReference<LambdaForm>[] lambdaForms;
59     //
60     // Indexes into lambdaForms:
61     //
62     // static final int LF_INVVIRTUAL, LF_INVSTATIC, LF_INVSPECIAL, LF_NEWINVSPECIAL,
63     // LF_INVINTERFACE, LF_INVSTATIC_INIT, LF_INTERPRET, LF_REBIND, LF_DELEGATE,
64     // LF_DELEGATE_BLOCK_INLINING, LF_EX_LINKER, LF_EX_INVOKER, LF_GEN_LINKER, LF_GEN_INVOKER,
65     // LF_CS_LINKER, LF_MH_LINKER, LF_GWC, LF_GWT, LF_LIMIT;
66 
67     /** Return the type corresponding uniquely (1-1) to this MT-form.
68      *  It might have any primitive returns or arguments, but will have no references except Object.
69      */
erasedType()70     public MethodType erasedType() {
71         return erasedType;
72     }
73 
74     /** Return the basic type derived from the erased type of this MT-form.
75      *  A basic type is erased (all references Object) and also has all primitive
76      *  types (except int, long, float, double, void) normalized to int.
77      *  Such basic types correspond to low-level JVM calling sequences.
78      */
basicType()79     public MethodType basicType() {
80         return basicType;
81     }
82 
assertIsBasicType()83     private boolean assertIsBasicType() {
84         // primitives must be flattened also
85         assert(erasedType == basicType)
86                 : "erasedType: " + erasedType + " != basicType: " + basicType;
87         return true;
88     }
89 
90     // Android-changed: Removed caches for MethodHandle adaptors / LambdaForms.
91     //
92     // public MethodHandle cachedMethodHandle(int which);
93     // synchronized public MethodHandle setCachedMethodHandle(int which, MethodHandle mh);
94     // public LambdaForm cachedLambdaForm(int which);
95     // synchronized public LambdaForm setCachedLambdaForm(int which, LambdaForm form);
96 
97     /**
98      * Build an MTF for a given type, which must have all references erased to Object.
99      * This MTF will stand for that type and all un-erased variations.
100      * Eagerly compute some basic properties of the type, common to all variations.
101      */
102     @SuppressWarnings({"rawtypes", "unchecked"})
MethodTypeForm(MethodType erasedType)103     protected MethodTypeForm(MethodType erasedType) {
104         this.erasedType = erasedType;
105 
106         Class<?>[] ptypes = erasedType.ptypes();
107         int ptypeCount = ptypes.length;
108         int pslotCount = ptypeCount;            // temp. estimate
109         int rtypeCount = 1;                     // temp. estimate
110         int rslotCount = 1;                     // temp. estimate
111 
112         int[] argToSlotTab = null, slotToArgTab = null;
113 
114         // Walk the argument types, looking for primitives.
115         int pac = 0, lac = 0, prc = 0, lrc = 0;
116         Class<?>[] epts = ptypes;
117         Class<?>[] bpts = epts;
118         for (int i = 0; i < epts.length; i++) {
119             Class<?> pt = epts[i];
120             if (pt != Object.class) {
121                 ++pac;
122                 Wrapper w = Wrapper.forPrimitiveType(pt);
123                 if (w.isDoubleWord())  ++lac;
124                 if (w.isSubwordOrInt() && pt != int.class) {
125                     if (bpts == epts)
126                         bpts = bpts.clone();
127                     bpts[i] = int.class;
128                 }
129             }
130         }
131         pslotCount += lac;                  // #slots = #args + #longs
132         Class<?> rt = erasedType.returnType();
133         Class<?> bt = rt;
134         if (rt != Object.class) {
135             ++prc;          // even void.class counts as a prim here
136             Wrapper w = Wrapper.forPrimitiveType(rt);
137             if (w.isDoubleWord())  ++lrc;
138             if (w.isSubwordOrInt() && rt != int.class)
139                 bt = int.class;
140             // adjust #slots, #args
141             if (rt == void.class)
142                 rtypeCount = rslotCount = 0;
143             else
144                 rslotCount += lrc;
145         }
146         if (epts == bpts && bt == rt) {
147             this.basicType = erasedType;
148         } else {
149             this.basicType = MethodType.makeImpl(bt, bpts, true);
150             // fill in rest of data from the basic type:
151             MethodTypeForm that = this.basicType.form();
152             assert(this != that);
153             this.primCounts = that.primCounts;
154             this.argCounts = that.argCounts;
155             this.argToSlotTable = that.argToSlotTable;
156             this.slotToArgTable = that.slotToArgTable;
157             // Android-changed: Removed cached adaptors / lambda forms.
158             //
159             // this.methodHandles = null;
160             // this.lambdaForms = null;
161             return;
162         }
163         if (lac != 0) {
164             int slot = ptypeCount + lac;
165             slotToArgTab = new int[slot+1];
166             argToSlotTab = new int[1+ptypeCount];
167             argToSlotTab[0] = slot;  // argument "-1" is past end of slots
168             for (int i = 0; i < epts.length; i++) {
169                 Class<?> pt = epts[i];
170                 Wrapper w = Wrapper.forBasicType(pt);
171                 if (w.isDoubleWord())  --slot;
172                 --slot;
173                 slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
174                 argToSlotTab[1+i]  = slot;
175             }
176             assert(slot == 0);  // filled the table
177         } else if (pac != 0) {
178             // have primitives but no long primitives; share slot counts with generic
179             assert(ptypeCount == pslotCount);
180             MethodTypeForm that = MethodType.genericMethodType(ptypeCount).form();
181             assert(this != that);
182             slotToArgTab = that.slotToArgTable;
183             argToSlotTab = that.argToSlotTable;
184         } else {
185             int slot = ptypeCount; // first arg is deepest in stack
186             slotToArgTab = new int[slot+1];
187             argToSlotTab = new int[1+ptypeCount];
188             argToSlotTab[0] = slot;  // argument "-1" is past end of slots
189             for (int i = 0; i < ptypeCount; i++) {
190                 --slot;
191                 slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
192                 argToSlotTab[1+i]  = slot;
193             }
194         }
195         this.primCounts = pack(lrc, prc, lac, pac);
196         this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
197         this.argToSlotTable = argToSlotTab;
198         this.slotToArgTable = slotToArgTab;
199 
200         if (pslotCount >= 256)  throw newIllegalArgumentException("too many arguments");
201 
202         // Initialize caches, but only for basic types
203         assert(basicType == erasedType);
204         // Android-changed: Removed cached adaptors / lambda forms.
205         //
206         // this.lambdaForms   = new SoftReference[LF_LIMIT];
207         // this.methodHandles = new SoftReference[MH_LIMIT];
208     }
209 
pack(int a, int b, int c, int d)210     private static long pack(int a, int b, int c, int d) {
211         assert(((a|b|c|d) & ~0xFFFF) == 0);
212         long hw = ((a << 16) | b), lw = ((c << 16) | d);
213         return (hw << 32) | lw;
214     }
unpack(long packed, int word)215     private static char unpack(long packed, int word) { // word==0 => return a, ==3 => return d
216         assert(word <= 3);
217         return (char)(packed >> ((3-word) * 16));
218     }
219 
parameterCount()220     public int parameterCount() {                      // # outgoing values
221         return unpack(argCounts, 3);
222     }
parameterSlotCount()223     public int parameterSlotCount() {                  // # outgoing interpreter slots
224         return unpack(argCounts, 2);
225     }
returnCount()226     public int returnCount() {                         // = 0 (V), or 1
227         return unpack(argCounts, 1);
228     }
returnSlotCount()229     public int returnSlotCount() {                     // = 0 (V), 2 (J/D), or 1
230         return unpack(argCounts, 0);
231     }
primitiveParameterCount()232     public int primitiveParameterCount() {
233         return unpack(primCounts, 3);
234     }
longPrimitiveParameterCount()235     public int longPrimitiveParameterCount() {
236         return unpack(primCounts, 2);
237     }
primitiveReturnCount()238     public int primitiveReturnCount() {                // = 0 (obj), or 1
239         return unpack(primCounts, 1);
240     }
longPrimitiveReturnCount()241     public int longPrimitiveReturnCount() {            // = 1 (J/D), or 0
242         return unpack(primCounts, 0);
243     }
hasPrimitives()244     public boolean hasPrimitives() {
245         return primCounts != 0;
246     }
hasNonVoidPrimitives()247     public boolean hasNonVoidPrimitives() {
248         if (primCounts == 0)  return false;
249         if (primitiveParameterCount() != 0)  return true;
250         return (primitiveReturnCount() != 0 && returnCount() != 0);
251     }
hasLongPrimitives()252     public boolean hasLongPrimitives() {
253         return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0;
254     }
parameterToArgSlot(int i)255     public int parameterToArgSlot(int i) {
256         return argToSlotTable[1+i];
257     }
argSlotToParameter(int argSlot)258     public int argSlotToParameter(int argSlot) {
259         // Note:  Empty slots are represented by zero in this table.
260         // Valid arguments slots contain incremented entries, so as to be non-zero.
261         // We return -1 the caller to mean an empty slot.
262         return slotToArgTable[argSlot] - 1;
263     }
264 
findForm(MethodType mt)265     static MethodTypeForm findForm(MethodType mt) {
266         MethodType erased = canonicalize(mt, ERASE, ERASE);
267         if (erased == null) {
268             // It is already erased.  Make a new MethodTypeForm.
269             return new MethodTypeForm(mt);
270         } else {
271             // Share the MethodTypeForm with the erased version.
272             return erased.form();
273         }
274     }
275 
276     /** Codes for {@link #canonicalize(java.lang.Class, int)}.
277      * ERASE means change every reference to {@code Object}.
278      * WRAP means convert primitives (including {@code void} to their
279      * corresponding wrapper types.  UNWRAP means the reverse of WRAP.
280      * INTS means convert all non-void primitive types to int or long,
281      * according to size.  LONGS means convert all non-void primitives
282      * to long, regardless of size.  RAW_RETURN means convert a type
283      * (assumed to be a return type) to int if it is smaller than an int,
284      * or if it is void.
285      */
286     public static final int NO_CHANGE = 0, ERASE = 1, WRAP = 2, UNWRAP = 3, INTS = 4, LONGS = 5, RAW_RETURN = 6;
287 
288     /** Canonicalize the types in the given method type.
289      * If any types change, intern the new type, and return it.
290      * Otherwise return null.
291      */
canonicalize(MethodType mt, int howRet, int howArgs)292     public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) {
293         Class<?>[] ptypes = mt.ptypes();
294         Class<?>[] ptc = MethodTypeForm.canonicalizeAll(ptypes, howArgs);
295         Class<?> rtype = mt.returnType();
296         Class<?> rtc = MethodTypeForm.canonicalize(rtype, howRet);
297         if (ptc == null && rtc == null) {
298             // It is already canonical.
299             return null;
300         }
301         // Find the erased version of the method type:
302         if (rtc == null)  rtc = rtype;
303         if (ptc == null)  ptc = ptypes;
304         return MethodType.makeImpl(rtc, ptc, true);
305     }
306 
307     /** Canonicalize the given return or param type.
308      *  Return null if the type is already canonicalized.
309      */
canonicalize(Class<?> t, int how)310     static Class<?> canonicalize(Class<?> t, int how) {
311         Class<?> ct;
312         if (t == Object.class) {
313             // no change, ever
314         } else if (!t.isPrimitive()) {
315             switch (how) {
316                 case UNWRAP:
317                     ct = Wrapper.asPrimitiveType(t);
318                     if (ct != t)  return ct;
319                     break;
320                 case RAW_RETURN:
321                 case ERASE:
322                     return Object.class;
323             }
324         } else if (t == void.class) {
325             // no change, usually
326             switch (how) {
327                 case RAW_RETURN:
328                     return int.class;
329                 case WRAP:
330                     return Void.class;
331             }
332         } else {
333             // non-void primitive
334             switch (how) {
335                 case WRAP:
336                     return Wrapper.asWrapperType(t);
337                 case INTS:
338                     if (t == int.class || t == long.class)
339                         return null;  // no change
340                     if (t == double.class)
341                         return long.class;
342                     return int.class;
343                 case LONGS:
344                     if (t == long.class)
345                         return null;  // no change
346                     return long.class;
347                 case RAW_RETURN:
348                     if (t == int.class || t == long.class ||
349                         t == float.class || t == double.class)
350                         return null;  // no change
351                     // everything else returns as an int
352                     return int.class;
353             }
354         }
355         // no change; return null to signify
356         return null;
357     }
358 
359     /** Canonicalize each param type in the given array.
360      *  Return null if all types are already canonicalized.
361      */
canonicalizeAll(Class<?>[] ts, int how)362     static Class<?>[] canonicalizeAll(Class<?>[] ts, int how) {
363         Class<?>[] cs = null;
364         for (int imax = ts.length, i = 0; i < imax; i++) {
365             Class<?> c = canonicalize(ts[i], how);
366             if (c == void.class)
367                 c = null;  // a Void parameter was unwrapped to void; ignore
368             if (c != null) {
369                 if (cs == null)
370                     cs = ts.clone();
371                 cs[i] = c;
372             }
373         }
374         return cs;
375     }
376 
377     @Override
toString()378     public String toString() {
379         return "Form"+erasedType;
380     }
381 }
382