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