2 * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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.
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
29 import sun.dyn.util.Wrapper;
32 * Shared information for a group of method types, which differ
33 * only by reference types, and therefore share a common erasure
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.
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
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
62 public MethodType erasedType() {
66 public static MethodTypeImpl of(MethodType type) {
67 return METHOD_TYPE_FRIEND.form(type);
70 /** Access methods for the internals of MethodType, supplied to
71 * MethodTypeForm as a trusted agent.
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);
82 public static void setMethodTypeFriend(Access token, MethodTypeFriend am) {
84 if (METHOD_TYPE_FRIEND != null)
85 throw new InternalError(); // just once
86 METHOD_TYPE_FRIEND = am;
88 static private MethodTypeFriend METHOD_TYPE_FRIEND;
90 protected MethodTypeImpl(MethodType erasedType) {
91 this.erasedType = erasedType;
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
99 int[] argToSlotTab = null, slotToArgTab = null;
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());
109 if (hasTwoArgSlots(pt)) ++lac;
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;
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;
132 slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
133 argToSlotTab[1+i] = slot;
135 assert(slot == 0); // filled the table
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++) {
146 slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
147 argToSlotTab[1+i] = slot;
150 this.argToSlotTable = argToSlotTab;
151 this.slotToArgTable = slotToArgTab;
153 if (pslotCount >= 256) throw new IllegalArgumentException("too many arguments");
155 // send a few bits down to the JVM:
156 this.vmslots = parameterSlotCount();
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;
168 /** Turn all primitive types to corresponding wrapper types.
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;
179 /** Turn all primitive argument types to corresponding wrapper types.
180 * Subword and void return types are promoted to int.
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;
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.
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;
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.
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;
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;
225 int pac = primitiveParameterCount();
227 return primsAtEnd = t;
229 int argc = parameterCount();
230 int lac = longPrimitiveParameterCount();
231 if (pac == argc && (lac == 0 || lac == argc))
232 return primsAtEnd = t;
234 // known to have a mix of 2 or 3 of ref, int, long
235 return primsAtEnd = reorderParameters(t, primsAtEndOrder(t), null);
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.
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}.
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
258 int argc = form.parameterCount();
259 int[] paramOrder = new int[argc];
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;
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];
271 if (!pt.isPrimitive()) ord = rfill++;
272 else if (!hasTwoArgSlots(pt)) ord = ifill++;
274 if (ord != i) changed = true;
277 assert(rfill == argc - pac && ifill == argc - lac && lfill == argc);
279 form.primsAtEnd = form.erasedType;
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.
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;
300 if (ord < ptypes.length) nt = ptypes[ord];
301 else if (ord == ordMax) nt = mt.returnType();
302 else nt = moreParams[ord - ptypes.length];
305 if (!changed) return mt;
306 return METHOD_TYPE_FRIEND.makeImpl(mt.returnType(), ntypes, true);
309 private static boolean hasTwoArgSlots(Class<?> type) {
310 return type == long.class || type == double.class;
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;
318 private static char unpack(long packed, int word) { // word==0 => return a, ==3 => return d
320 return (char)(packed >> ((3-word) * 16));
323 public int parameterCount() { // # outgoing values
324 return unpack(argCounts, 3);
326 public int parameterSlotCount() { // # outgoing interpreter slots
327 return unpack(argCounts, 2);
329 public int returnCount() { // = 0 (V), or 1
330 return unpack(argCounts, 1);
332 public int returnSlotCount() { // = 0 (V), 2 (J/D), or 1
333 return unpack(argCounts, 0);
335 public int primitiveParameterCount() {
336 return unpack(primCounts, 3);
338 public int longPrimitiveParameterCount() {
339 return unpack(primCounts, 2);
341 public int primitiveReturnCount() { // = 0 (obj), or 1
342 return unpack(primCounts, 1);
344 public int longPrimitiveReturnCount() { // = 1 (J/D), or 0
345 return unpack(primCounts, 0);
347 public boolean hasPrimitives() {
348 return primCounts != 0;
350 // public boolean hasNonVoidPrimitives() {
351 // if (primCounts == 0) return false;
352 // if (primitiveParameterCount() != 0) return true;
353 // return (primitiveReturnCount() != 0 && returnCount() != 0);
355 public boolean hasLongPrimitives() {
356 return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0;
358 public int parameterToArgSlot(int i) {
359 return argToSlotTable[1+i];
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;
368 public static void initForm(Access token, MethodType mt) {
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);
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);
384 // Share the MethodTypeForm with the erased version.
385 return METHOD_TYPE_FRIEND.form(erased);
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,
399 public static final int NO_CHANGE = 0, ERASE = 1, WRAP = 2, UNWRAP = 3, INTS = 4, LONGS = 5, RAW_RETURN = 6;
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.
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.
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);
420 /** Canonicalize the given return or param type.
421 * Return null if the type is already canonicalized.
423 static Class<?> canonicalize(Class<?> t, int how) {
425 if (t == Object.class) {
427 } else if (!t.isPrimitive()) {
430 ct = Wrapper.asPrimitiveType(t);
431 if (ct != t) return ct;
437 } else if (t == void.class) {
438 // no change, usually
446 // non-void primitive
449 return Wrapper.asWrapperType(t);
451 if (t == int.class || t == long.class)
452 return null; // no change
453 if (t == double.class)
458 return null; // no change
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
468 // no change; return null to signify
472 /** Canonicalize each param type in the given array.
473 * Return null if all types are already canonicalized.
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);
488 public static Invokers invokers(Access token, MethodType type) {
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);
498 public String toString() {
499 return "Form"+erasedType;