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