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 
 34 /**
 35  * Shared information for a group of method types, which differ
 36  * only by reference types, and therefore share a common erasure
 37  * and wrapping.
 38  * <p>
 39  * For an empirical discussion of the structure of method types,
 40  * see <a href="http://groups.google.com/group/jvm-languages/browse_thread/thread/ac9308ae74da9b7e/">
 41  * the thread "Avoiding Boxing" on jvm-languages</a>.
 42  * There are approximately 2000 distinct erased method types in the JDK.
 43  * There are a little over 10 times that number of unerased types.
 44  * No more than half of these are likely to be loaded at once.
 45  * @author John Rose
 46  */
 47 final class MethodTypeForm {
 48     final short parameterSlotCount;
 49     final short primitiveCount;
 50     final MethodType erasedType;        // the canonical erasure
 51     final MethodType basicType;         // the canonical erasure, with primitives simplified
 52 
 53     // Cached adapter information:
 54     final SoftReference<MethodHandle>[] methodHandles;
 55 
 56     // Indexes into methodHandles:
 57     static final int
 58             MH_BASIC_INV      =  0,  // cached instance of MH.invokeBasic
 59             MH_NF_INV         =  1,  // cached helper for LF.NamedFunction
 60             MH_UNINIT_CS      =  2,  // uninitialized call site
 61             MH_LIMIT          =  3;
 62 
 63     // Cached lambda form information, for basic types only:
 64     final SoftReference<LambdaForm>[] lambdaForms;
 65 
 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_INVSPECIAL_IFC          = 20,  // DMH invokeSpecial of (private) interface method
 89             LF_INVNATIVE               = 21,  // NMH invokeNative
 90             LF_VH_EX_INVOKER           = 22,  // VarHandle exact invoker
 91             LF_VH_GEN_INVOKER          = 23,  // VarHandle generic invoker
 92             LF_VH_GEN_LINKER           = 24,  // VarHandle generic linker
 93             LF_COLLECTOR               = 25,  // collector handle
 94             LF_LIMIT                   = 26;
 95 
 96     /** Return the type corresponding uniquely (1-1) to this MT-form.
 97      *  It might have any primitive returns or arguments, but will have no references except Object.
 98      */
 99     public MethodType erasedType() {
100         return erasedType;
101     }
102 
103     /** Return the basic type derived from the erased type of this MT-form.
104      *  A basic type is erased (all references Object) and also has all primitive
105      *  types (except int, long, float, double, void) normalized to int.
106      *  Such basic types correspond to low-level JVM calling sequences.
107      */
108     public MethodType basicType() {
109         return basicType;
110     }
111 
112     public MethodHandle cachedMethodHandle(int which) {
113         SoftReference<MethodHandle> entry = methodHandles[which];
114         return (entry != null) ? entry.get() : null;
115     }
116 
117     public synchronized MethodHandle setCachedMethodHandle(int which, MethodHandle mh) {
118         // Simulate a CAS, to avoid racy duplication of results.
119         SoftReference<MethodHandle> entry = methodHandles[which];
120         if (entry != null) {
121             MethodHandle prev = entry.get();
122             if (prev != null) {
123                 return prev;
124             }
125         }
126         methodHandles[which] = new SoftReference<>(mh);
127         return mh;
128     }
129 
130     public LambdaForm cachedLambdaForm(int which) {
131         SoftReference<LambdaForm> entry = lambdaForms[which];
132         return (entry != null) ? entry.get() : null;
133     }
134 
135     public synchronized LambdaForm setCachedLambdaForm(int which, LambdaForm form) {
136         // Simulate a CAS, to avoid racy duplication of results.
137         SoftReference<LambdaForm> entry = lambdaForms[which];
138         if (entry != null) {
139             LambdaForm prev = entry.get();
140             if (prev != null) {
141                 return prev;
142             }
143         }
144         lambdaForms[which] = new SoftReference<>(form);
145         return form;
146     }
147 
148     /**
149      * Build an MTF for a given type, which must have all references erased to Object.
150      * This MTF will stand for that type and all un-erased variations.
151      * Eagerly compute some basic properties of the type, common to all variations.
152      */
153     @SuppressWarnings({"rawtypes", "unchecked"})
154     protected MethodTypeForm(MethodType erasedType) {
155         this.erasedType = erasedType;
156 
157         Class<?>[] ptypes = erasedType.ptypes();
158         int pslotCount = ptypes.length;
159 
160         // Walk the argument types, looking for primitives.
161         short primitiveCount = 0, longArgCount = 0;
162         Class<?>[] erasedPtypes = ptypes;
163         Class<?>[] basicPtypes = erasedPtypes;
164         for (int i = 0; i < erasedPtypes.length; i++) {
165             Class<?> ptype = erasedPtypes[i];
166             if (ptype != Object.class) {
167                 ++primitiveCount;
168                 Wrapper w = Wrapper.forPrimitiveType(ptype);
169                 if (w.isDoubleWord())  ++longArgCount;
170                 if (w.isSubwordOrInt() && ptype != int.class) {
171                     if (basicPtypes == erasedPtypes)
172                         basicPtypes = basicPtypes.clone();
173                     basicPtypes[i] = int.class;
174                 }
175             }
176         }
177         pslotCount += longArgCount;                  // #slots = #args + #longs
178         Class<?> returnType = erasedType.returnType();
179         Class<?> basicReturnType = returnType;
180         if (returnType != Object.class) {
181             ++primitiveCount; // even void.class counts as a prim here
182             Wrapper w = Wrapper.forPrimitiveType(returnType);
183             if (w.isSubwordOrInt() && returnType != int.class)
184                 basicReturnType = int.class;
185         }
186         if (erasedPtypes == basicPtypes && basicReturnType == returnType) {
187             // Basic type
188             this.basicType = erasedType;
189 
190             if (pslotCount >= 256)  throw newIllegalArgumentException("too many arguments");
191 
192             this.primitiveCount = primitiveCount;
193             this.parameterSlotCount = (short)pslotCount;
194             this.lambdaForms   = new SoftReference[LF_LIMIT];
195             this.methodHandles = new SoftReference[MH_LIMIT];
196         } else {
197             this.basicType = MethodType.methodType(basicReturnType, basicPtypes, true);
198             // fill in rest of data from the basic type:
199             MethodTypeForm that = this.basicType.form();
200             assert(this != that);
201 
202             this.parameterSlotCount = that.parameterSlotCount;
203             this.primitiveCount = that.primitiveCount;
204             this.methodHandles = null;
205             this.lambdaForms = null;
206         }
207     }
208 
209     public int parameterCount() {
210         return erasedType.parameterCount();
211     }
212     public int parameterSlotCount() {
213         return parameterSlotCount;
214     }
215     public boolean hasPrimitives() {
216         return primitiveCount != 0;
217     }
218 
219     static MethodTypeForm findForm(MethodType mt) {
220         MethodType erased = canonicalize(mt, ERASE);
221         if (erased == null) {
222             // It is already erased.  Make a new MethodTypeForm.
223             return new MethodTypeForm(mt);
224         } else {
225             // Share the MethodTypeForm with the erased version.
226             return erased.form();
227         }
228     }
229 
230     /** Codes for {@link #canonicalize(java.lang.Class, int)}.
231      * ERASE means change every reference to {@code Object}.
232      * WRAP means convert primitives (including {@code void} to their
233      * corresponding wrapper types.  UNWRAP means the reverse of WRAP.
234      */
235     public static final int ERASE = 1, WRAP = 2, UNWRAP = 3;
236 
237     /** Canonicalize the types in the given method type.
238      * If any types change, intern the new type, and return it.
239      * Otherwise return null.
240      */
241     public static MethodType canonicalize(MethodType mt, int how) {
242         Class<?>[] ptypes = mt.ptypes();
243         Class<?>[] ptypesCanonical = canonicalizeAll(ptypes, how);
244         Class<?> rtype = mt.returnType();
245         Class<?> rtypeCanonical = canonicalize(rtype, how);
246         if (ptypesCanonical == null && rtypeCanonical == null) {
247             // It is already canonical.
248             return null;
249         }
250         // Find the erased version of the method type:
251         if (rtypeCanonical == null)  rtypeCanonical = rtype;
252         if (ptypesCanonical == null)  ptypesCanonical = ptypes;
253         return MethodType.methodType(rtypeCanonical, ptypesCanonical, true);
254     }
255 
256     /** Canonicalize the given return or param type.
257      *  Return null if the type is already canonicalized.
258      */
259     static Class<?> canonicalize(Class<?> t, int how) {
260         if (t == Object.class) {
261             // no change, ever
262         } else if (!t.isPrimitive()) {
263             switch (how) {
264                 case UNWRAP:
265                     Class<?> ct = Wrapper.asPrimitiveType(t);
266                     if (ct != t)  return ct;
267                     break;
268                 case ERASE:
269                     return Object.class;
270             }
271         } else if (how == WRAP) {
272             return Wrapper.asWrapperType(t);
273         }
274         // no change; return null to signify
275         return null;
276     }
277 
278     /** Canonicalize each param type in the given array.
279      *  Return null if all types are already canonicalized.
280      */
281     static Class<?>[] canonicalizeAll(Class<?>[] ts, int how) {
282         Class<?>[] cs = null;
283         for (int imax = ts.length, i = 0; i < imax; i++) {
284             Class<?> c = canonicalize(ts[i], how);
285             // Void parameters may be unwrapped to void; ignore those
286             if (c != null && c != void.class) {
287                 if (cs == null)
288                     cs = ts.clone();
289                 cs[i] = c;
290             }
291         }
292         return cs;
293     }
294 
295     @Override
296     public String toString() {
297         return "Form"+erasedType;
298     }
299 }