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