1 /*
  2  * Copyright (c) 2008, 2022, 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 
 30 import java.lang.ref.SoftReference;
 31 
 32 import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
 33 import static java.lang.invoke.MethodHandleStatics.NO_SOFT_CACHE;
 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  */
 48 final class MethodTypeForm {
 49     final short parameterSlotCount;
 50     final short primitiveCount;
 51     final MethodType erasedType;        // the canonical erasure
 52     final MethodType basicType;         // the canonical erasure, with primitives simplified
 53 
 54     // Cached adapter information:
 55     private final Object[] methodHandles;
 56 
 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     private final Object[] lambdaForms;
 66 
 67     // Indexes into lambdaForms:
 68     static final int
 69             LF_INVVIRTUAL              =  0,  // DMH invokeVirtual
 70             LF_INVSTATIC               =  1,
 71             LF_INVSPECIAL              =  2,
 72             LF_NEWINVSPECIAL           =  3,
 73             LF_INVINTERFACE            =  4,
 74             LF_INVSTATIC_INIT          =  5,  // DMH invokeStatic with <clinit> barrier
 75             LF_INTERPRET               =  6,  // LF interpreter
 76             LF_REBIND                  =  7,  // BoundMethodHandle
 77             LF_DELEGATE                =  8,  // DelegatingMethodHandle
 78             LF_DELEGATE_BLOCK_INLINING =  9,  // Counting DelegatingMethodHandle w/ @DontInline
 79             LF_EX_LINKER               = 10,  // invokeExact_MT (for invokehandle)
 80             LF_EX_INVOKER              = 11,  // MHs.invokeExact
 81             LF_GEN_LINKER              = 12,  // generic invoke_MT (for invokehandle)
 82             LF_GEN_INVOKER             = 13,  // generic MHs.invoke
 83             LF_CS_LINKER               = 14,  // linkToCallSite_CS
 84             LF_MH_LINKER               = 15,  // linkToCallSite_MH
 85             LF_GWC                     = 16,  // guardWithCatch (catchException)
 86             LF_GWT                     = 17,  // guardWithTest
 87             LF_TF                      = 18,  // tryFinally
 88             LF_LOOP                    = 19,  // loop
 89             LF_INVSPECIAL_IFC          = 20,  // DMH invokeSpecial of (private) interface method
 90             LF_INVNATIVE               = 21,  // NMH invokeNative
 91             LF_VH_EX_INVOKER           = 22,  // VarHandle exact invoker
 92             LF_VH_GEN_INVOKER          = 23,  // VarHandle generic invoker
 93             LF_VH_GEN_LINKER           = 24,  // VarHandle generic linker
 94             LF_COLLECTOR               = 25,  // collector handle
 95             LF_LIMIT                   = 26;
 96 
 97     /** Return the type corresponding uniquely (1-1) to this MT-form.
 98      *  It might have any primitive returns or arguments, but will have no references except Object.
 99      */
100     public MethodType erasedType() {
101         return erasedType;
102     }
103 
104     /** Return the basic type derived from the erased type of this MT-form.
105      *  A basic type is erased (all references Object) and also has all primitive
106      *  types (except int, long, float, double, void) normalized to int.
107      *  Such basic types correspond to low-level JVM calling sequences.
108      */
109     public MethodType basicType() {
110         return basicType;
111     }
112 
113     @SuppressWarnings({"rawtypes", "unchecked"})
114     public MethodHandle cachedMethodHandle(int which) {
115         Object entry = methodHandles[which];
116         if (entry == null) {
117             return null;
118         } else if (entry instanceof MethodHandle) {
119             return (MethodHandle) entry;
120         } else {
121             return ((SoftReference<MethodHandle>)entry).get();
122         }
123     }
124 
125     public synchronized MethodHandle setCachedMethodHandle(int which, MethodHandle mh) {
126         // Simulate a CAS, to avoid racy duplication of results.
127         MethodHandle prev = cachedMethodHandle(which);
128         if (prev != null) {
129             return prev;
130         }
131         if (NO_SOFT_CACHE) {
132             methodHandles[which] = mh;
133         } else {
134             methodHandles[which] = new SoftReference<>(mh);
135         }
136         return mh;
137     }
138 
139     @SuppressWarnings({"rawtypes", "unchecked"})
140     public LambdaForm cachedLambdaForm(int which) {
141         Object entry = lambdaForms[which];
142         if (entry == null) {
143             return null;
144         } else if (entry instanceof LambdaForm) {
145             return (LambdaForm) entry;
146         } else {
147             return ((SoftReference<LambdaForm>)entry).get();
148         }
149     }
150 
151     public synchronized LambdaForm setCachedLambdaForm(int which, LambdaForm form) {
152         // Simulate a CAS, to avoid racy duplication of results.
153         LambdaForm prev = cachedLambdaForm(which);
154         if (prev != null) {
155             return prev;
156         }
157         if (NO_SOFT_CACHE) {
158             lambdaForms[which] = form;
159         } else {
160             lambdaForms[which] = new SoftReference<>(form);
161         }
162         return form;
163     }
164 
165     /**
166      * Build an MTF for a given type, which must have all references erased to Object.
167      * This MTF will stand for that type and all un-erased variations.
168      * Eagerly compute some basic properties of the type, common to all variations.
169      */
170     @SuppressWarnings({"rawtypes", "unchecked"})
171     protected MethodTypeForm(MethodType erasedType) {
172         this.erasedType = erasedType;
173 
174         Class<?>[] ptypes = erasedType.ptypes();
175         int pslotCount = ptypes.length;
176 
177         // Walk the argument types, looking for primitives.
178         short primitiveCount = 0, longArgCount = 0;
179         Class<?>[] erasedPtypes = ptypes;
180         Class<?>[] basicPtypes = erasedPtypes;
181         for (int i = 0; i < erasedPtypes.length; i++) {
182             Class<?> ptype = erasedPtypes[i];
183             if (ptype != Object.class) {
184                 ++primitiveCount;
185                 Wrapper w = Wrapper.forPrimitiveType(ptype);
186                 if (w.isDoubleWord())  ++longArgCount;
187                 if (w.isSubwordOrInt() && ptype != int.class) {
188                     if (basicPtypes == erasedPtypes)
189                         basicPtypes = basicPtypes.clone();
190                     basicPtypes[i] = int.class;
191                 }
192             }
193         }
194         pslotCount += longArgCount;                  // #slots = #args + #longs
195         Class<?> returnType = erasedType.returnType();
196         Class<?> basicReturnType = returnType;
197         if (returnType != Object.class) {
198             ++primitiveCount; // even void.class counts as a prim here
199             Wrapper w = Wrapper.forPrimitiveType(returnType);
200             if (w.isSubwordOrInt() && returnType != int.class)
201                 basicReturnType = int.class;
202         }
203         if (erasedPtypes == basicPtypes && basicReturnType == returnType) {
204             // Basic type
205             this.basicType = erasedType;
206 
207             if (pslotCount >= 256)  throw newIllegalArgumentException("too many arguments");
208 
209             this.primitiveCount = primitiveCount;
210             this.parameterSlotCount = (short)pslotCount;
211             this.lambdaForms   = new Object[LF_LIMIT];
212             this.methodHandles = new Object[MH_LIMIT];
213         } else {
214             this.basicType = MethodType.methodType(basicReturnType, basicPtypes, true);
215             // fill in rest of data from the basic type:
216             MethodTypeForm that = this.basicType.form();
217             assert(this != that);
218 
219             this.parameterSlotCount = that.parameterSlotCount;
220             this.primitiveCount = that.primitiveCount;
221             this.methodHandles = null;
222             this.lambdaForms = null;
223         }
224     }
225 
226     public int parameterCount() {
227         return erasedType.parameterCount();
228     }
229     public int parameterSlotCount() {
230         return parameterSlotCount;
231     }
232     public boolean hasPrimitives() {
233         return primitiveCount != 0;
234     }
235 
236     static MethodTypeForm findForm(MethodType mt) {
237         MethodType erased = canonicalize(mt, ERASE);
238         if (erased == null) {
239             // It is already erased.  Make a new MethodTypeForm.
240             return new MethodTypeForm(mt);
241         } else {
242             // Share the MethodTypeForm with the erased version.
243             return erased.form();
244         }
245     }
246 
247     /** Codes for {@link #canonicalize(java.lang.Class, int)}.
248      * ERASE means change every reference to {@code Object}.
249      * WRAP means convert primitives (including {@code void} to their
250      * corresponding wrapper types.  UNWRAP means the reverse of WRAP.
251      */
252     public static final int ERASE = 1, WRAP = 2, UNWRAP = 3;
253 
254     /** Canonicalize the types in the given method type.
255      * If any types change, intern the new type, and return it.
256      * Otherwise return null.
257      */
258     public static MethodType canonicalize(MethodType mt, int how) {
259         Class<?>[] ptypes = mt.ptypes();
260         Class<?>[] ptypesCanonical = canonicalizeAll(ptypes, how);
261         Class<?> rtype = mt.returnType();
262         Class<?> rtypeCanonical = canonicalize(rtype, how);
263         if (ptypesCanonical == null && rtypeCanonical == null) {
264             // It is already canonical.
265             return null;
266         }
267         // Find the erased version of the method type:
268         if (rtypeCanonical == null)  rtypeCanonical = rtype;
269         if (ptypesCanonical == null)  ptypesCanonical = ptypes;
270         return MethodType.methodType(rtypeCanonical, ptypesCanonical, true);
271     }
272 
273     /** Canonicalize the given return or param type.
274      *  Return null if the type is already canonicalized.
275      */
276     static Class<?> canonicalize(Class<?> t, int how) {
277         if (t == Object.class) {
278             // no change, ever
279         } else if (!t.isPrimitive()) {
280             switch (how) {
281                 case UNWRAP:
282                     Class<?> ct = Wrapper.asPrimitiveType(t);
283                     if (ct != t)  return ct;
284                     break;
285                 case ERASE:
286                     return Object.class;
287             }
288         } else if (how == WRAP) {
289             return Wrapper.asWrapperType(t);
290         }
291         // no change; return null to signify
292         return null;
293     }
294 
295     /** Canonicalize each param type in the given array.
296      *  Return null if all types are already canonicalized.
297      */
298     static Class<?>[] canonicalizeAll(Class<?>[] ts, int how) {
299         Class<?>[] cs = null;
300         for (int imax = ts.length, i = 0; i < imax; i++) {
301             Class<?> c = canonicalize(ts[i], how);
302             // Void parameters may be unwrapped to void; ignore those
303             if (c != null && c != void.class) {
304                 if (cs == null)
305                     cs = ts.clone();
306                 cs[i] = c;
307             }
308         }
309         return cs;
310     }
311 
312     @Override
313     public String toString() {
314         return "Form"+erasedType;
315     }
316 }