1 /*
  2  * Copyright (c) 2016, 2025, 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 jdk.internal.vm.annotation.AOTSafeClassInitializer;
 29 import sun.invoke.util.Wrapper;
 30 import sun.util.logging.PlatformLogger;
 31 
 32 import java.lang.classfile.Annotation;
 33 import java.lang.classfile.ClassFile;
 34 import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
 35 import java.lang.classfile.attribute.SourceFileAttribute;
 36 import java.lang.constant.ClassDesc;
 37 import java.util.ArrayList;
 38 import java.util.HashSet;
 39 import java.util.Map;
 40 import java.util.Objects;
 41 import java.util.Set;
 42 import java.util.TreeMap;
 43 import java.util.TreeSet;
 44 import java.util.stream.Stream;
 45 
 46 import static java.lang.classfile.ClassFile.*;
 47 import static java.lang.invoke.LambdaForm.BasicType.*;
 48 import static java.lang.invoke.LambdaForm.Kind.*;
 49 import static java.lang.invoke.MethodTypeForm.*;
 50 
 51 /// Generates bound method handle species classes, and classes with methods that
 52 /// hold compiled lambda form bytecode ahead of time, so certain lambda forms
 53 /// no longer need to spin classes because they can find existing bytecode.
 54 /// Bytecode pre-generation reduces static initialization costs, footprint costs,
 55 /// and circular dependencies that may arise if a class is generated per
 56 /// LambdaForm by [InvokerBytecodeGenerator].
 57 ///
 58 /// Since lambda forms and bound method handle species are closely tied to
 59 /// method types, which have many varieties, this generator needs *traces* to
 60 /// detect which method types are used, so generation matches the actual usage.
 61 /// See the main entrypoint [#generateHolderClasses(Stream)] for more details
 62 /// about *traces*.
 63 ///
 64 /// Note this pregeneration does not cover all lambda forms that can be created.
 65 /// For example, forms created by [LambdaFormEditor] are not captured.
 66 ///
 67 /// Pregenerated species classes are resolved in [ClassSpecializer.Factory#loadSpecies]
 68 /// and behave identically to on-demand generated ones.  Pregenerated lambda
 69 /// forms are resolved in [InvokerBytecodeGenerator#lookupPregenerated], which
 70 /// looks up methods for code from the following 4 possibly-generated classes:
 71 ///  -  [Invokers.Holder]
 72 ///  -  [DirectMethodHandle.Holder]
 73 ///  -  [DelegatingMethodHandle.Holder]
 74 ///  -  [LambdaForm.Holder]
 75 ///
 76 /// [VarHandle] linker forms, analogous to invoker forms in [Invokers.Holder],
 77 /// have a similar pre-generation system except it is done at source generation;
 78 /// they reside in [VarHandleGuards].
 79 ///
 80 /// ## Usages of this generator
 81 /// Currently, `GenerateJLIClassesHelper` is invoked when creating a modular JDK
 82 /// image or generating an AOT cache.
 83 ///
 84 /// #### Modular Image
 85 /// When creating a modular JDK image,
 86 /// `jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin` passes the
 87 /// *traces* in the file `jdk/tools/jlink/internal/plugins/default_jli_trace.txt`
 88 /// in `$JAVA_HOME/lib/modules` to this generator.  The *traces* are generated
 89 /// from the execution of `build.tools.classlist.HelloClasslist` in the build
 90 /// process of the JDK.
 91 ///
 92 /// > To list all the Species classes in a JDK image:
 93 /// > ```
 94 /// > jimage list $JAVA_HOME/lib/modules | grep BoundMethodHandle.Species_
 95 /// > ```
 96 ///
 97 /// > All these pregenerated classes can be examined by javap in the same image:
 98 /// > (Note to escape `$` in bash)
 99 /// > ```
100 /// > javap -c -p -v java.lang.invoke.LambdaForm\$Holder
101 /// > ```
102 ///
103 /// #### AOT Cache
104 /// When creating an AOT cache, *traces* generated from the training run are
105 /// captured and stored inside the AOT configuration file, and are accessed with
106 /// the C++ `FinalImageRecipes` class.  Classes regenerated from these *traces*
107 /// are linked in assembly phase; see `regeneratedClasses.hpp`.
108 ///
109 /// @see #generateHolderClasses(Stream)
110 /// @see BoundMethodHandle.Specializer
111 /// @see DelegatingMethodHandle.Holder
112 /// @see DirectMethodHandle.Holder
113 /// @see Invokers.Holder
114 /// @see LambdaForm.Holder
115 /// @see VarHandleGuards
116 final class GenerateJLIClassesHelper {
117     // Map from DirectMethodHandle method type name to index to LambdaForms
118     static final Map<String, Integer> DMH_METHOD_TYPE_MAP =
119             Map.of(
120                     DIRECT_INVOKE_VIRTUAL.methodName,     LF_INVVIRTUAL,
121                     DIRECT_INVOKE_STATIC.methodName,      LF_INVSTATIC,
122                     DIRECT_INVOKE_SPECIAL.methodName,     LF_INVSPECIAL,
123                     DIRECT_NEW_INVOKE_SPECIAL.methodName, LF_NEWINVSPECIAL,
124                     DIRECT_INVOKE_INTERFACE.methodName,   LF_INVINTERFACE,
125                     DIRECT_INVOKE_STATIC_INIT.methodName, LF_INVSTATIC_INIT,
126                     DIRECT_INVOKE_SPECIAL_IFC.methodName, LF_INVSPECIAL_IFC
127             );
128 
129     static final String DIRECT_HOLDER = "java/lang/invoke/DirectMethodHandle$Holder";
130     static final String DELEGATING_HOLDER = "java/lang/invoke/DelegatingMethodHandle$Holder";
131     static final String BASIC_FORMS_HOLDER = "java/lang/invoke/LambdaForm$Holder";
132     static final String INVOKERS_HOLDER = "java/lang/invoke/Invokers$Holder";
133     static final String INVOKERS_HOLDER_CLASS_NAME = INVOKERS_HOLDER.replace('/', '.');
134     static final String BMH_SPECIES_PREFIX = "java.lang.invoke.BoundMethodHandle$Species_";
135     static final Annotation AOT_SAFE_ANNOTATION = Annotation.of(AOTSafeClassInitializer.class.describeConstable().orElseThrow());
136 
137     static class HolderClassBuilder {
138 
139 
140         private final TreeSet<String> speciesTypes = new TreeSet<>();
141         private final TreeSet<String> invokerTypes = new TreeSet<>();
142         private final TreeSet<String> callSiteTypes = new TreeSet<>();
143         private final Map<String, Set<String>> dmhMethods = new TreeMap<>();
144 
145         HolderClassBuilder addSpeciesType(String type) {
146             speciesTypes.add(expandSignature(type));
147             return this;
148         }
149 
150         HolderClassBuilder addInvokerType(String methodType) {
151             validateMethodType(methodType);
152             invokerTypes.add(methodType);
153             return this;
154         }
155 
156         HolderClassBuilder addCallSiteType(String csType) {
157             validateMethodType(csType);
158             callSiteTypes.add(csType);
159             return this;
160         }
161 
162         Map<String, byte[]> build() {
163             int count = 0;
164             for (Set<String> entry : dmhMethods.values()) {
165                 count += entry.size();
166             }
167             MethodType[] directMethodTypes = new MethodType[count];
168             int[] dmhTypes = new int[count];
169             int index = 0;
170             for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
171                 String dmhType = entry.getKey();
172                 for (String type : entry.getValue()) {
173                     // The DMH type to actually ask for is retrieved by removing
174                     // the first argument, which needs to be of Object.class
175                     MethodType mt = asMethodType(type);
176                     if (mt.parameterCount() < 1 ||
177                             mt.parameterType(0) != Object.class) {
178                         throw new RuntimeException(
179                                 "DMH type parameter must start with L: " + dmhType + " " + type);
180                     }
181                     // Adapt the method type of the LF to retrieve
182                     directMethodTypes[index] = mt.dropParameterTypes(0, 1);
183                     // invokeVirtual and invokeInterface must have a leading Object
184                     // parameter, i.e., the receiver
185                     dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType);
186                     if (dmhTypes[index] == LF_INVINTERFACE || dmhTypes[index] == LF_INVVIRTUAL) {
187                         if (mt.parameterCount() < 2 ||
188                                 mt.parameterType(1) != Object.class) {
189                             throw new RuntimeException(
190                                     "DMH type parameter must start with LL: " + dmhType + " " + type);
191                         }
192                     }
193                     index++;
194                 }
195             }
196 
197             // The invoker type to ask for is retrieved by removing the first
198             // and the last argument, which needs to be of Object.class
199             MethodType[] invokerMethodTypes = new MethodType[invokerTypes.size()];
200             index = 0;
201             for (String invokerType : invokerTypes) {
202                 MethodType mt = asMethodType(invokerType);
203                 final int lastParam = mt.parameterCount() - 1;
204                 if (!checkInvokerTypeParams(mt)) {
205                     throw new RuntimeException(
206                             "Invoker type parameter must start and end with Object: " + invokerType);
207                 }
208                 mt = mt.dropParameterTypes(lastParam, lastParam + 1);
209                 invokerMethodTypes[index] = mt.dropParameterTypes(0, 1);
210                 index++;
211             }
212 
213             // The callSite type to ask for is retrieved by removing the last
214             // argument, which needs to be of Object.class
215             MethodType[] callSiteMethodTypes = new MethodType[callSiteTypes.size()];
216             index = 0;
217             for (String callSiteType : callSiteTypes) {
218                 MethodType mt = asMethodType(callSiteType);
219                 final int lastParam = mt.parameterCount() - 1;
220                 if (mt.parameterCount() < 1 ||
221                         mt.parameterType(lastParam) != Object.class) {
222                     throw new RuntimeException(
223                             "CallSite type parameter must end with Object: " + callSiteType);
224                 }
225                 callSiteMethodTypes[index] = mt.dropParameterTypes(lastParam, lastParam + 1);
226                 index++;
227             }
228             Map<String, byte[]> result = new TreeMap<>();
229             result.put(DIRECT_HOLDER,
230                        generateDirectMethodHandleHolderClassBytes(
231                             DIRECT_HOLDER, directMethodTypes, dmhTypes));
232             result.put(DELEGATING_HOLDER,
233                        generateDelegatingMethodHandleHolderClassBytes(
234                             DELEGATING_HOLDER, directMethodTypes));
235             result.put(INVOKERS_HOLDER,
236                        generateInvokersHolderClassBytes(INVOKERS_HOLDER,
237                             invokerMethodTypes, callSiteMethodTypes));
238             result.put(BASIC_FORMS_HOLDER,
239                        generateBasicFormsClassBytes(BASIC_FORMS_HOLDER));
240 
241             speciesTypes.forEach(types -> {
242                 Map.Entry<String, byte[]> entry = generateConcreteBMHClassBytes(types);
243                 result.put(entry.getKey(), entry.getValue());
244             });
245             // clear builder
246             speciesTypes.clear();
247             invokerTypes.clear();
248             callSiteTypes.clear();
249             dmhMethods.clear();
250             return result;
251         }
252 
253         public static MethodType asMethodType(String basicSignatureString) {
254             String[] parts = basicSignatureString.split("_");
255             assert (parts.length == 2);
256             assert (parts[1].length() == 1);
257             String parameters = expandSignature(parts[0]);
258             Class<?> rtype = simpleType(parts[1].charAt(0));
259             if (parameters.isEmpty()) {
260                 return MethodType.methodType(rtype);
261             } else {
262                 Class<?>[] ptypes = new Class<?>[parameters.length()];
263                 for (int i = 0; i < ptypes.length; i++) {
264                     ptypes[i] = simpleType(parameters.charAt(i));
265                 }
266                 return MethodType.methodType(rtype, ptypes);
267             }
268         }
269 
270         public static boolean checkInvokerTypeParams(MethodType mt) {
271             final int lastParam = mt.parameterCount() - 1;
272             return (mt.parameterCount() >= 2 &&
273                     mt.parameterType(0) == Object.class &&
274                     mt.parameterType(lastParam) == Object.class);
275         }
276 
277         private void addDMHMethodType(String dmh, String methodType) {
278             validateMethodType(methodType);
279             Set<String> methodTypes = dmhMethods.get(dmh);
280             if (methodTypes == null) {
281                 methodTypes = new TreeSet<>();
282                 dmhMethods.put(dmh, methodTypes);
283             }
284             methodTypes.add(methodType);
285         }
286 
287         private static void validateMethodType(String type) {
288             String[] typeParts = type.split("_");
289             // check return type (second part)
290             if (typeParts.length != 2 || typeParts[1].length() != 1
291                     || !isBasicTypeChar(typeParts[1].charAt(0))) {
292                 throw new RuntimeException(
293                         "Method type signature must be of form [LJIFD]*_[LJIFDV]");
294             }
295             // expand and check arguments (first part)
296             expandSignature(typeParts[0]);
297         }
298 
299         // Convert LL -> LL, L3 -> LLL
300         private static String expandSignature(String signature) {
301             StringBuilder sb = new StringBuilder();
302             char last = 'X';
303             int count = 0;
304             for (int i = 0; i < signature.length(); i++) {
305                 char c = signature.charAt(i);
306                 if (c >= '0' && c <= '9') {
307                     count *= 10;
308                     count += (c - '0');
309                 } else {
310                     requireBasicType(c);
311                     for (int j = 1; j < count; j++) {
312                         sb.append(last);
313                     }
314                     sb.append(c);
315                     last = c;
316                     count = 0;
317                 }
318             }
319 
320             // ended with a number, e.g., "L2": append last char count - 1 times
321             if (count > 1) {
322                 requireBasicType(last);
323                 for (int j = 1; j < count; j++) {
324                     sb.append(last);
325                 }
326             }
327             return sb.toString();
328         }
329 
330         private static void requireBasicType(char c) {
331             if (!isArgBasicTypeChar(c)) {
332                 throw new RuntimeException(
333                         "Character " + c + " must correspond to a basic field type: LIJFD");
334             }
335         }
336 
337         private static Class<?> simpleType(char c) {
338             if (isBasicTypeChar(c)) {
339                 return LambdaForm.BasicType.basicType(c).basicTypeClass();
340             }
341             switch (c) {
342                 case 'Z':
343                 case 'B':
344                 case 'S':
345                 case 'C':
346                     throw new IllegalArgumentException("Not a valid primitive: " + c +
347                             " (use I instead)");
348                 default:
349                     throw new IllegalArgumentException("Not a primitive: " + c);
350             }
351         }
352     }
353 
354     /// Returns a map from class names in internal form to the corresponding
355     /// class bytes.
356     ///
357     /// A few known lambda forms, such as field accessors, can be comprehensively
358     /// generated.  Most others lambda forms are associated with unique method
359     /// types; thus they are generated per the given stream of SPECIES_RESOLVE
360     /// and LF_RESOLVE *trace* logs, which are created according to {@link
361     /// MethodHandleStatics#TRACE_RESOLVE} configuration.
362     ///
363     /// The names of methods in the generated classes are internal tokens
364     /// recognized by [InvokerBytecodeGenerator#lookupPregenerated] and are
365     /// subject to change.
366     ///
367     /// @param traces the *traces* to determine the lambda forms and species
368     ///        to generate
369     /// @see MethodHandleStatics#traceLambdaForm
370     /// @see MethodHandleStatics#traceSpeciesType
371     static Map<String, byte[]> generateHolderClasses(Stream<String> traces)  {
372         Objects.requireNonNull(traces);
373         HolderClassBuilder builder = new HolderClassBuilder();
374         traces.map(line -> line.split(" "))
375                 .forEach(parts -> {
376                     switch (parts[0]) {
377                         case "[SPECIES_RESOLVE]":
378                             // Allow for new types of species data classes being resolved here
379                             assert parts.length >= 2;
380                             if (parts[1].startsWith(BMH_SPECIES_PREFIX)) {
381                                 String species = parts[1].substring(BMH_SPECIES_PREFIX.length());
382                                 if (!"L".equals(species)) {
383                                     builder.addSpeciesType(species);
384                                 }
385                             }
386                             break;
387                         case "[LF_RESOLVE]":
388                             assert parts.length > 3;
389                             String methodType = parts[3];
390                             if (parts[1].equals(INVOKERS_HOLDER_CLASS_NAME)) {
391                                 if ("linkToTargetMethod".equals(parts[2]) ||
392                                         "linkToCallSite".equals(parts[2])) {
393                                     builder.addCallSiteType(methodType);
394                                 } else {
395                                     MethodType mt = HolderClassBuilder.asMethodType(methodType);
396                                     // Work around JDK-8327499
397                                     if (HolderClassBuilder.checkInvokerTypeParams(mt)) {
398                                         builder.addInvokerType(methodType);
399                                     } else {
400                                         PlatformLogger.getLogger("java.lang.invoke")
401                                                 .warning("Invalid LF_RESOLVE " + parts[1] + " " + parts[2] + " " + parts[3]);
402                                     }
403                                 }
404                             } else if (parts[1].contains("DirectMethodHandle")) {
405                                 String dmh = parts[2];
406                                 // ignore getObject etc for now (generated by default)
407                                 if (DMH_METHOD_TYPE_MAP.containsKey(dmh)) {
408                                     builder.addDMHMethodType(dmh, methodType);
409                                 }
410                             }
411                             break;
412                         default:
413                             break; // ignore
414                     }
415                 });
416 
417         return builder.build();
418     }
419 
420     /**
421      * Returns a {@code byte[]} representation of a class implementing
422      * the zero and identity forms of all {@code LambdaForm.BasicType}s.
423      */
424     static byte[] generateBasicFormsClassBytes(String className) {
425         ArrayList<LambdaForm> forms = new ArrayList<>();
426         ArrayList<String> names = new ArrayList<>();
427         HashSet<String> dedupSet = new HashSet<>();
428         for (LambdaForm.BasicType type : LambdaForm.BasicType.values()) {
429             String name;
430 
431             LambdaForm identity = LambdaForm.identityForm(type);
432             name = identity.kind.defaultLambdaName
433                    + "_" + identity.returnType().basicTypeChar();
434             if (dedupSet.add(name)) {
435                 names.add(name);
436                 forms.add(identity);
437             }
438 
439             if (type != V_TYPE) {
440                 LambdaForm constant = LambdaForm.constantForm(type);
441                 name = constant.kind.defaultLambdaName
442                         + "_" + constant.returnType().basicTypeChar();
443                 if (dedupSet.add(name)) {
444                     names.add(name);
445                     forms.add(constant);
446                 }
447             }
448         }
449         return generateCodeBytesForLFs(className,
450                 names.toArray(new String[0]),
451                 forms.toArray(new LambdaForm[0]));
452     }
453 
454     /**
455      * Returns a {@code byte[]} representation of a class implementing
456      * DirectMethodHandle of each pairwise combination of {@code MethodType} and
457      * an {@code int} representing method type.
458      */
459     static byte[] generateDirectMethodHandleHolderClassBytes(String className,
460             MethodType[] methodTypes, int[] types) {
461         ArrayList<LambdaForm> forms = new ArrayList<>();
462         ArrayList<String> names = new ArrayList<>();
463         for (int i = 0; i < methodTypes.length; i++) {
464             // invokeVirtual and invokeInterface must have a leading Object
465             // parameter, i.e., the receiver
466             if (types[i] == LF_INVVIRTUAL || types[i] == LF_INVINTERFACE) {
467                 if (methodTypes[i].parameterCount() < 1 ||
468                         methodTypes[i].parameterType(0) != Object.class) {
469                     throw new InternalError("Invalid method type for " +
470                             (types[i] == LF_INVVIRTUAL ? "invokeVirtual" : "invokeInterface") +
471                             " DMH, needs at least two leading reference arguments: " +
472                             methodTypes[i]);
473                 }
474             }
475 
476             LambdaForm form = DirectMethodHandle.makePreparedLambdaForm(methodTypes[i], types[i]);
477             forms.add(form);
478             names.add(form.kind.defaultLambdaName);
479         }
480         for (Wrapper wrapper : Wrapper.values()) {
481             int ftype = wrapper == Wrapper.VOID ? DirectMethodHandle.FT_CHECKED_REF : DirectMethodHandle.ftypeKind(wrapper.primitiveType());
482             for (byte b = DirectMethodHandle.AF_GETFIELD; b < DirectMethodHandle.AF_LIMIT; b++) {
483                 LambdaForm form = DirectMethodHandle
484                         .makePreparedFieldLambdaForm(b, /*isVolatile*/false, ftype);
485                 if (form.kind == GENERIC)
486                     throw new InternalError(b + " non-volatile " + ftype);
487                 forms.add(form);
488                 names.add(form.kind.defaultLambdaName);
489                 // volatile
490                 form = DirectMethodHandle
491                         .makePreparedFieldLambdaForm(b, /*isVolatile*/true, ftype);
492                 if (form.kind == GENERIC)
493                     throw new InternalError(b + " volatile " + ftype);
494                 forms.add(form);
495                 names.add(form.kind.defaultLambdaName);
496             }
497         }
498         return generateCodeBytesForLFs(className,
499                 names.toArray(new String[0]),
500                 forms.toArray(new LambdaForm[0]));
501     }
502 
503     /**
504      * Returns a {@code byte[]} representation of a class implementing
505      * DelegatingMethodHandles of each {@code MethodType} kind in the
506      * {@code methodTypes} argument.
507      */
508     static byte[] generateDelegatingMethodHandleHolderClassBytes(String className,
509             MethodType[] methodTypes) {
510 
511         HashSet<MethodType> dedupSet = new HashSet<>();
512         ArrayList<LambdaForm> forms = new ArrayList<>();
513         ArrayList<String> names = new ArrayList<>();
514         for (int i = 0; i < methodTypes.length; i++) {
515             // generate methods representing the DelegatingMethodHandle
516             if (dedupSet.add(methodTypes[i])) {
517                 // reinvokers are variant with the associated SpeciesData
518                 // and shape of the target LF, but we can easily pregenerate
519                 // the basic reinvokers associated with Species_L. Ultimately we
520                 // may want to consider pregenerating more of these, which will
521                 // require an even more complex naming scheme
522                 LambdaForm reinvoker = makeReinvokerFor(methodTypes[i]);
523                 forms.add(reinvoker);
524                 String speciesSig = BoundMethodHandle.speciesDataFor(reinvoker).key();
525                 assert(speciesSig.equals("L"));
526                 names.add(reinvoker.kind.defaultLambdaName + "_" + speciesSig);
527 
528                 LambdaForm delegate = makeDelegateFor(methodTypes[i]);
529                 forms.add(delegate);
530                 names.add(delegate.kind.defaultLambdaName);
531             }
532         }
533         return generateCodeBytesForLFs(className,
534                 names.toArray(new String[0]),
535                 forms.toArray(new LambdaForm[0]));
536     }
537 
538     /**
539      * Returns a {@code byte[]} representation of a class implementing
540      * the invoker forms for the set of supplied {@code invokerMethodTypes}
541      * and {@code callSiteMethodTypes}.
542      */
543     static byte[] generateInvokersHolderClassBytes(String className,
544             MethodType[] invokerMethodTypes, MethodType[] callSiteMethodTypes) {
545 
546         HashSet<MethodType> dedupSet = new HashSet<>();
547         ArrayList<LambdaForm> forms = new ArrayList<>();
548         ArrayList<String> names = new ArrayList<>();
549         int[] types = {
550             MethodTypeForm.LF_EX_LINKER,
551             MethodTypeForm.LF_EX_INVOKER,
552             MethodTypeForm.LF_GEN_LINKER,
553             MethodTypeForm.LF_GEN_INVOKER
554         };
555 
556         for (int i = 0; i < invokerMethodTypes.length; i++) {
557             // generate methods representing invokers of the specified type
558             if (dedupSet.add(invokerMethodTypes[i])) {
559                 for (int type : types) {
560                     LambdaForm invokerForm = Invokers.invokeHandleForm(invokerMethodTypes[i],
561                             /*customized*/false, type);
562                     forms.add(invokerForm);
563                     names.add(invokerForm.kind.defaultLambdaName);
564                 }
565             }
566         }
567 
568         dedupSet = new HashSet<>();
569         for (int i = 0; i < callSiteMethodTypes.length; i++) {
570             // generate methods representing invokers of the specified type
571             if (dedupSet.add(callSiteMethodTypes[i])) {
572                 LambdaForm callSiteForm = Invokers.callSiteForm(callSiteMethodTypes[i], true);
573                 forms.add(callSiteForm);
574                 names.add(callSiteForm.kind.defaultLambdaName);
575 
576                 LambdaForm methodHandleForm = Invokers.callSiteForm(callSiteMethodTypes[i], false);
577                 forms.add(methodHandleForm);
578                 names.add(methodHandleForm.kind.defaultLambdaName);
579             }
580         }
581 
582         return generateCodeBytesForLFs(className,
583                 names.toArray(new String[0]),
584                 forms.toArray(new LambdaForm[0]));
585     }
586 
587     /*
588      * Generate customized code for a set of LambdaForms of specified types into
589      * a class with a specified name.
590      */
591     private static byte[] generateCodeBytesForLFs(String className, String[] names, LambdaForm[] forms) {
592         return ClassFile.of().build(ClassDesc.ofInternalName(className), clb -> {
593             clb.withFlags(ACC_PRIVATE | ACC_FINAL | ACC_SUPER)
594                .withSuperclass(InvokerBytecodeGenerator.INVOKER_SUPER_DESC)
595                .with(RuntimeVisibleAnnotationsAttribute.of(AOT_SAFE_ANNOTATION))
596                .with(SourceFileAttribute.of(className.substring(className.lastIndexOf('/') + 1)));
597             for (int i = 0; i < forms.length; i++) {
598                 new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType()).addMethod(clb, false);
599             }
600         });
601     }
602 
603     private static LambdaForm makeReinvokerFor(MethodType type) {
604         MethodHandle emptyHandle = MethodHandles.empty(type);
605         return DelegatingMethodHandle.makeReinvokerForm(emptyHandle,
606                 MethodTypeForm.LF_REBIND,
607                 BoundMethodHandle.speciesData_L(),
608                 BoundMethodHandle.speciesData_L().getterFunction(0));
609     }
610 
611     private static LambdaForm makeDelegateFor(MethodType type) {
612         MethodHandle handle = MethodHandles.empty(type);
613         return DelegatingMethodHandle.makeReinvokerForm(
614                 handle,
615                 MethodTypeForm.LF_DELEGATE,
616                 DelegatingMethodHandle.class,
617                 DelegatingMethodHandle.NF_getTarget);
618     }
619 
620     /**
621      * Returns a {@code byte[]} representation of {@code BoundMethodHandle}
622      * species class implementing the signature defined by {@code types}.
623      */
624     @SuppressWarnings({"rawtypes", "unchecked"})
625     static Map.Entry<String, byte[]> generateConcreteBMHClassBytes(final String types) {
626         for (char c : types.toCharArray()) {
627             if (!isArgBasicTypeChar(c)) {
628                 throw new IllegalArgumentException("All characters must "
629                         + "correspond to a basic field type: LIJFD");
630             }
631         }
632         final BoundMethodHandle.SpeciesData species = BoundMethodHandle.SPECIALIZER.findSpecies(types);
633         final String className = species.speciesCode().getName();
634         final ClassSpecializer.Factory factory = BoundMethodHandle.SPECIALIZER.factory();
635         final byte[] code = factory.generateConcreteSpeciesCodeFile(className, species);
636         return Map.entry(className.replace('.', '/'), code);
637     }
638 
639 }