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.List;
 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> linkerTypes = new TreeSet<>();
143         private final TreeSet<String> callSiteTypes = new TreeSet<>();
144         private final Map<String, Set<String>> dmhMethods = new TreeMap<>();
145 
146         HolderClassBuilder addSpeciesType(String type) {
147             speciesTypes.add(expandSignature(type));
148             return this;
149         }
150 
151         HolderClassBuilder addInvokerType(String methodType) {
152             validateMethodType(methodType);
153             invokerTypes.add(methodType);
154             return this;
155         }
156 
157         HolderClassBuilder addLinkerType(String methodType) {
158             validateMethodType(methodType);
159             linkerTypes.add(methodType);
160             return this;
161         }
162 
163         HolderClassBuilder addCallSiteType(String csType) {
164             validateMethodType(csType);
165             callSiteTypes.add(csType);
166             return this;
167         }
168 
169         Map<String, byte[]> build() {
170             int count = 0;
171             for (Set<String> entry : dmhMethods.values()) {
172                 count += entry.size();
173             }
174             MethodType[] directMethodTypes = new MethodType[count];
175             int[] dmhTypes = new int[count];
176             int index = 0;
177             for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
178                 String dmhType = entry.getKey();
179                 for (String type : entry.getValue()) {
180                     // The DMH type to actually ask for is retrieved by removing
181                     // the first argument, which needs to be of Object.class
182                     MethodType mt = asMethodType(type);
183                     if (mt.parameterCount() < 1 ||
184                             mt.parameterType(0) != Object.class) {
185                         throw new RuntimeException(
186                                 "DMH type parameter must start with L: " + dmhType + " " + type);
187                     }
188 
189                     // Adapt the method type of the LF to retrieve
190                     directMethodTypes[index] = mt.dropParameterTypes(0, 1);
191 
192                     // invokeVirtual and invokeInterface must have a leading Object
193                     // parameter, i.e., the receiver
194                     dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType);
195                     if (dmhTypes[index] == LF_INVINTERFACE || dmhTypes[index] == LF_INVVIRTUAL) {
196                         if (mt.parameterCount() < 2 ||
197                                 mt.parameterType(1) != Object.class) {
198                             throw new RuntimeException(
199                                     "DMH type parameter must start with LL: " + dmhType + " " + type);
200                         }
201                     }
202                     index++;
203                 }
204             }
205 
206             // The linker type to ask for is retrieved by removing the first
207             // and the last argument, which needs to be of Object.class
208             MethodType[] linkerMethodTypes = new MethodType[linkerTypes.size()];
209             index = 0;
210             for (String linkerType : linkerTypes) {
211                 MethodType mt = asMethodType(linkerType);
212                 final int lastParam = mt.parameterCount() - 1;
213                 if (!checkLinkerTypeParams(mt)) {
214                     throw new RuntimeException(
215                             "Linker type parameter must start and end with Object: " + linkerType);
216                 }
217                 mt = mt.dropParameterTypes(lastParam, lastParam + 1);
218                 linkerMethodTypes[index] = mt.dropParameterTypes(0, 1);
219                 index++;
220             }
221 
222             // The invoker type to ask for is retrieved by removing the first
223             // argument, which needs to be of Object.class
224             MethodType[] invokerMethodTypes = new MethodType[invokerTypes.size()];
225             index = 0;
226             for (String invokerType : invokerTypes) {
227                 MethodType mt = asMethodType(invokerType);
228                 if (!checkInvokerTypeParams(mt)) {
229                     throw new RuntimeException(
230                             "Invoker type parameter must start with 2 Objects: " + invokerType);
231                 }
232                 invokerMethodTypes[index] = mt.dropParameterTypes(0, 2);
233                 index++;
234             }
235 
236             // The callSite type to ask for is retrieved by removing the last
237             // argument, which needs to be of Object.class
238             MethodType[] callSiteMethodTypes = new MethodType[callSiteTypes.size()];
239             index = 0;
240             for (String callSiteType : callSiteTypes) {
241                 MethodType mt = asMethodType(callSiteType);
242                 final int lastParam = mt.parameterCount() - 1;
243                 if (mt.parameterCount() < 1 ||
244                         mt.parameterType(lastParam) != Object.class) {
245                     throw new RuntimeException(
246                             "CallSite type parameter must end with Object: " + callSiteType);
247                 }
248                 callSiteMethodTypes[index] = mt.dropParameterTypes(lastParam, lastParam + 1);
249                 index++;
250             }
251 
252             Map<String, byte[]> result = new TreeMap<>();
253             result.put(DIRECT_HOLDER,
254                        generateDirectMethodHandleHolderClassBytes(
255                             DIRECT_HOLDER, directMethodTypes, dmhTypes));
256             result.put(DELEGATING_HOLDER,
257                        generateDelegatingMethodHandleHolderClassBytes(
258                             DELEGATING_HOLDER, directMethodTypes));
259             result.put(INVOKERS_HOLDER,
260                        generateInvokersHolderClassBytes(INVOKERS_HOLDER,
261                             linkerMethodTypes, invokerMethodTypes, callSiteMethodTypes));
262             result.put(BASIC_FORMS_HOLDER,
263                        generateBasicFormsClassBytes(BASIC_FORMS_HOLDER));
264 
265             speciesTypes.forEach(types -> {
266                 Map.Entry<String, byte[]> entry = generateConcreteBMHClassBytes(types);
267                 result.put(entry.getKey(), entry.getValue());
268             });
269 
270             // clear builder
271             speciesTypes.clear();
272             invokerTypes.clear();
273             callSiteTypes.clear();
274             dmhMethods.clear();
275 
276             return result;
277         }
278 
279         public static MethodType asMethodType(String basicSignatureString) {
280             String[] parts = basicSignatureString.split("_");
281             assert (parts.length == 2);
282             assert (parts[1].length() == 1);
283             String parameters = expandSignature(parts[0]);
284             Class<?> rtype = simpleType(parts[1].charAt(0));
285             if (parameters.isEmpty()) {
286                 return MethodType.methodType(rtype);
287             } else {
288                 Class<?>[] ptypes = new Class<?>[parameters.length()];
289                 for (int i = 0; i < ptypes.length; i++) {
290                     ptypes[i] = simpleType(parameters.charAt(i));
291                 }
292                 return MethodType.methodType(rtype, ptypes);
293             }
294         }
295 
296         public static boolean checkInvokerTypeParams(MethodType mt) {
297             return (mt.parameterCount() >= 2 &&
298                     mt.parameterType(0) == Object.class &&
299                     mt.parameterType(1) == Object.class);
300         }
301 
302         public static boolean checkLinkerTypeParams(MethodType mt) {
303             final int lastParam = mt.parameterCount() - 1;
304             return (mt.parameterCount() >= 2 &&
305                     mt.parameterType(0) == Object.class &&
306                     mt.parameterType(lastParam) == Object.class);
307         }
308 
309         private void addDMHMethodType(String dmh, String methodType) {
310             validateMethodType(methodType);
311             Set<String> methodTypes = dmhMethods.get(dmh);
312             if (methodTypes == null) {
313                 methodTypes = new TreeSet<>();
314                 dmhMethods.put(dmh, methodTypes);
315             }
316             methodTypes.add(methodType);
317         }
318 
319         private static void validateMethodType(String type) {
320             String[] typeParts = type.split("_");
321             // check return type (second part)
322             if (typeParts.length != 2 || typeParts[1].length() != 1
323                     || !isBasicTypeChar(typeParts[1].charAt(0))) {
324                 throw new RuntimeException(
325                         "Method type signature must be of form [LJIFD]*_[LJIFDV]");
326             }
327             // expand and check arguments (first part)
328             expandSignature(typeParts[0]);
329         }
330 
331         // Convert LL -> LL, L3 -> LLL
332         private static String expandSignature(String signature) {
333             StringBuilder sb = new StringBuilder();
334             char last = 'X';
335             int count = 0;
336             for (int i = 0; i < signature.length(); i++) {
337                 char c = signature.charAt(i);
338                 if (c >= '0' && c <= '9') {
339                     count *= 10;
340                     count += (c - '0');
341                 } else {
342                     requireBasicType(c);
343                     for (int j = 1; j < count; j++) {
344                         sb.append(last);
345                     }
346                     sb.append(c);
347                     last = c;
348                     count = 0;
349                 }
350             }
351 
352             // ended with a number, e.g., "L2": append last char count - 1 times
353             if (count > 1) {
354                 requireBasicType(last);
355                 for (int j = 1; j < count; j++) {
356                     sb.append(last);
357                 }
358             }
359             return sb.toString();
360         }
361 
362         private static void requireBasicType(char c) {
363             if (!isArgBasicTypeChar(c)) {
364                 throw new RuntimeException(
365                         "Character " + c + " must correspond to a basic field type: LIJFD");
366             }
367         }
368 
369         private static Class<?> simpleType(char c) {
370             if (isBasicTypeChar(c)) {
371                 return LambdaForm.BasicType.basicType(c).basicTypeClass();
372             }
373             switch (c) {
374                 case 'Z':
375                 case 'B':
376                 case 'S':
377                 case 'C':
378                     throw new IllegalArgumentException("Not a valid primitive: " + c +
379                             " (use I instead)");
380                 default:
381                     throw new IllegalArgumentException("Not a primitive: " + c);
382             }
383         }
384     }
385 
386     /// Returns a map from class names in internal form to the corresponding
387     /// class bytes.
388     ///
389     /// A few known lambda forms, such as field accessors, can be comprehensively
390     /// generated.  Most others lambda forms are associated with unique method
391     /// types; thus they are generated per the given stream of SPECIES_RESOLVE
392     /// and LF_RESOLVE *trace* logs, which are created according to {@link
393     /// MethodHandleStatics#TRACE_RESOLVE} configuration.
394     ///
395     /// The names of methods in the generated classes are internal tokens
396     /// recognized by [InvokerBytecodeGenerator#lookupPregenerated] and are
397     /// subject to change.
398     ///
399     /// @param traces the *traces* to determine the lambda forms and species
400     ///        to generate
401     /// @see MethodHandleStatics#traceLambdaForm
402     /// @see MethodHandleStatics#traceSpeciesType
403     static Map<String, byte[]> generateHolderClasses(Stream<String> traces)  {
404         Objects.requireNonNull(traces);
405         HolderClassBuilder builder = new HolderClassBuilder();
406         traces.map(line -> line.split(" "))
407                 .forEach(parts -> {
408                     switch (parts[0]) {
409                         case "[SPECIES_RESOLVE]":
410                             // Allow for new types of species data classes being resolved here
411                             assert parts.length >= 2;
412                             if (parts[1].startsWith(BMH_SPECIES_PREFIX)) {
413                                 String species = parts[1].substring(BMH_SPECIES_PREFIX.length());
414                                 if (!"L".equals(species)) {
415                                     builder.addSpeciesType(species);
416                                 }
417                             }
418                             break;
419                         case "[LF_RESOLVE]":
420                             assert parts.length > 3;
421                             String methodType = parts[3];
422                             if (parts[1].equals(INVOKERS_HOLDER_CLASS_NAME)) {
423                                 if ("linkToTargetMethod".equals(parts[2]) ||
424                                         "linkToCallSite".equals(parts[2])) {
425                                     builder.addCallSiteType(methodType);
426                                 } else if (parts[2].endsWith("nvoker")) {
427                                     // MH.exactInvoker exactInvoker MH.invoker invoker
428                                     builder.addInvokerType(methodType);
429                                 } else {
430                                     builder.addLinkerType(methodType);
431                                 }
432                             } else if (parts[1].contains("DirectMethodHandle")) {
433                                 String dmh = parts[2];
434                                 // ignore getObject etc for now (generated by default)
435                                 if (DMH_METHOD_TYPE_MAP.containsKey(dmh)) {
436                                     builder.addDMHMethodType(dmh, methodType);
437                                 }
438                             }
439                             break;
440                         default:
441                             break; // ignore
442                     }
443                 });
444 
445         return builder.build();
446     }
447 
448     /**
449      * Returns a {@code byte[]} representation of a class implementing
450      * the zero and identity forms of all {@code LambdaForm.BasicType}s.
451      */
452     static byte[] generateBasicFormsClassBytes(String className) {
453         ArrayList<LambdaForm> forms = new ArrayList<>();
454         ArrayList<String> names = new ArrayList<>();
455         HashSet<String> dedupSet = new HashSet<>();
456         for (LambdaForm.BasicType type : LambdaForm.BasicType.values()) {
457             String name;
458 
459             LambdaForm identity = LambdaForm.identityForm(type);
460             name = identity.kind.defaultLambdaName
461                    + "_" + identity.returnType().basicTypeChar();
462             if (dedupSet.add(name)) {
463                 names.add(name);
464                 forms.add(identity);
465             }
466 
467             if (type != V_TYPE) {
468                 LambdaForm constant = LambdaForm.constantForm(type);
469                 name = constant.kind.defaultLambdaName
470                         + "_" + constant.returnType().basicTypeChar();
471                 if (dedupSet.add(name)) {
472                     names.add(name);
473                     forms.add(constant);
474                 }
475             }
476         }
477         return generateCodeBytesForLFs(className,
478                 names.toArray(new String[0]),
479                 forms.toArray(new LambdaForm[0]));
480     }
481 
482     /**
483      * Returns a {@code byte[]} representation of a class implementing
484      * DirectMethodHandle of each pairwise combination of {@code MethodType} and
485      * an {@code int} representing method type.
486      */
487     static byte[] generateDirectMethodHandleHolderClassBytes(String className,
488             MethodType[] methodTypes, int[] types) {
489         ArrayList<LambdaForm> forms = new ArrayList<>();
490         ArrayList<String> names = new ArrayList<>();
491         for (int i = 0; i < methodTypes.length; i++) {
492             // invokeVirtual and invokeInterface must have a leading Object
493             // parameter, i.e., the receiver
494             if (types[i] == LF_INVVIRTUAL || types[i] == LF_INVINTERFACE) {
495                 if (methodTypes[i].parameterCount() < 1 ||
496                         methodTypes[i].parameterType(0) != Object.class) {
497                     throw new InternalError("Invalid method type for " +
498                             (types[i] == LF_INVVIRTUAL ? "invokeVirtual" : "invokeInterface") +
499                             " DMH, needs at least two leading reference arguments: " +
500                             methodTypes[i]);
501                 }
502             }
503 
504             LambdaForm form = DirectMethodHandle.makePreparedLambdaForm(methodTypes[i], types[i]);
505             forms.add(form);
506             names.add(form.kind.defaultLambdaName);
507         }
508         record FieldLfToken(byte formOp, int ftypeKind) {}
509         List<FieldLfToken> tokens = new ArrayList<>();
510         for (int i = 0; i <= DirectMethodHandle.FT_CHECKED_REF; i++) {
511             for (byte formOp = DirectMethodHandle.AF_GETFIELD; formOp < DirectMethodHandle.AF_LIMIT; formOp++) {
512                 tokens.add(new FieldLfToken(formOp, i));
513             }
514         }
515         for (int i : new int[] {DirectMethodHandle.FT_UNCHECKED_NR_REF, DirectMethodHandle.FT_CHECKED_NR_REF}) {
516             for (byte formOp = DirectMethodHandle.AF_GETFIELD; formOp < DirectMethodHandle.AF_LIMIT; formOp++) {
517                 boolean isGetter = (formOp & 1) == (DirectMethodHandle.AF_GETFIELD & 1);
518                 if (!isGetter) {
519                     tokens.add(new FieldLfToken(formOp, i));
520                 }
521             }
522         }
523         // Only legal flat combinations; no static
524         tokens.add(new FieldLfToken(DirectMethodHandle.AF_GETFIELD, DirectMethodHandle.FT_NULLABLE_FLAT));
525         tokens.add(new FieldLfToken(DirectMethodHandle.AF_PUTFIELD, DirectMethodHandle.FT_NULLABLE_FLAT));
526         tokens.add(new FieldLfToken(DirectMethodHandle.AF_PUTFIELD, DirectMethodHandle.FT_NR_FLAT));
527         // Compile
528         for (var token : tokens) {
529             byte b = token.formOp;
530             int ftype = token.ftypeKind;
531             LambdaForm form = DirectMethodHandle
532                     .makePreparedFieldLambdaForm(b, /*isVolatile*/false, ftype);
533             if (form.kind == GENERIC)
534                 throw new InternalError(b + " non-volatile " + ftype);
535             forms.add(form);
536             names.add(form.kind.defaultLambdaName);
537             // volatile
538             form = DirectMethodHandle
539                     .makePreparedFieldLambdaForm(b, /*isVolatile*/true, ftype);
540             if (form.kind == GENERIC)
541                 throw new InternalError(b + " volatile " + ftype);
542             forms.add(form);
543             names.add(form.kind.defaultLambdaName);
544         }
545         return generateCodeBytesForLFs(className,
546                 names.toArray(new String[0]),
547                 forms.toArray(new LambdaForm[0]));
548     }
549 
550     /**
551      * Returns a {@code byte[]} representation of a class implementing
552      * DelegatingMethodHandles of each {@code MethodType} kind in the
553      * {@code methodTypes} argument.
554      */
555     static byte[] generateDelegatingMethodHandleHolderClassBytes(String className,
556             MethodType[] methodTypes) {
557 
558         HashSet<MethodType> dedupSet = new HashSet<>();
559         ArrayList<LambdaForm> forms = new ArrayList<>();
560         ArrayList<String> names = new ArrayList<>();
561         for (int i = 0; i < methodTypes.length; i++) {
562             // generate methods representing the DelegatingMethodHandle
563             if (dedupSet.add(methodTypes[i])) {
564                 // reinvokers are variant with the associated SpeciesData
565                 // and shape of the target LF, but we can easily pregenerate
566                 // the basic reinvokers associated with Species_L. Ultimately we
567                 // may want to consider pregenerating more of these, which will
568                 // require an even more complex naming scheme
569                 LambdaForm reinvoker = makeReinvokerFor(methodTypes[i]);
570                 forms.add(reinvoker);
571                 String speciesSig = BoundMethodHandle.speciesDataFor(reinvoker).key();
572                 assert(speciesSig.equals("L"));
573                 names.add(reinvoker.kind.defaultLambdaName + "_" + speciesSig);
574 
575                 LambdaForm delegate = makeDelegateFor(methodTypes[i]);
576                 forms.add(delegate);
577                 names.add(delegate.kind.defaultLambdaName);
578             }
579         }
580         return generateCodeBytesForLFs(className,
581                 names.toArray(new String[0]),
582                 forms.toArray(new LambdaForm[0]));
583     }
584 
585     /**
586      * Returns a {@code byte[]} representation of a class implementing
587      * the invoker forms for the set of supplied {@code linkerMethodTypes}
588      * {@code invokerMethodTypes}, and {@code callSiteMethodTypes}.
589      */
590     static byte[] generateInvokersHolderClassBytes(String className,
591             MethodType[] linkerMethodTypes, MethodType[] invokerMethodTypes,
592             MethodType[] callSiteMethodTypes) {
593 
594         HashSet<MethodType> dedupSet = new HashSet<>();
595         ArrayList<LambdaForm> forms = new ArrayList<>();
596         ArrayList<String> names = new ArrayList<>();
597 
598         int[] invokerTypes = {
599             MethodTypeForm.LF_EX_INVOKER,
600             MethodTypeForm.LF_GEN_INVOKER,
601         };
602 
603         for (MethodType methodType : invokerMethodTypes) {
604             // generate methods representing invokers of the specified type
605             if (dedupSet.add(methodType)) {
606                 for (int type : invokerTypes) {
607                     LambdaForm invokerForm = Invokers.invokeHandleForm(methodType,
608                             /*customized*/false, type);
609                     forms.add(invokerForm);
610                     names.add(invokerForm.kind.defaultLambdaName);
611                 }
612             }
613         }
614 
615         int[] linkerTypes = {
616                 MethodTypeForm.LF_EX_LINKER,
617                 MethodTypeForm.LF_GEN_LINKER,
618         };
619 
620         dedupSet = new HashSet<>();
621         for (MethodType methodType : linkerMethodTypes) {
622             // generate methods representing linkers of the specified type
623             if (dedupSet.add(methodType)) {
624                 for (int type : linkerTypes) {
625                     LambdaForm linkerForm = Invokers.invokeHandleForm(methodType,
626                             /*customized*/false, type);
627                     forms.add(linkerForm);
628                     names.add(linkerForm.kind.defaultLambdaName);
629                 }
630             }
631         }
632 
633         dedupSet = new HashSet<>();
634         for (int i = 0; i < callSiteMethodTypes.length; i++) {
635             // generate methods representing invokers of the specified type
636             if (dedupSet.add(callSiteMethodTypes[i])) {
637                 LambdaForm callSiteForm = Invokers.callSiteForm(callSiteMethodTypes[i], true);
638                 forms.add(callSiteForm);
639                 names.add(callSiteForm.kind.defaultLambdaName);
640 
641                 LambdaForm methodHandleForm = Invokers.callSiteForm(callSiteMethodTypes[i], false);
642                 forms.add(methodHandleForm);
643                 names.add(methodHandleForm.kind.defaultLambdaName);
644             }
645         }
646 
647         return generateCodeBytesForLFs(className,
648                 names.toArray(new String[0]),
649                 forms.toArray(new LambdaForm[0]));
650     }
651 
652     /*
653      * Generate customized code for a set of LambdaForms of specified types into
654      * a class with a specified name.
655      */
656     private static byte[] generateCodeBytesForLFs(String className, String[] names, LambdaForm[] forms) {
657         return ClassFile.of().build(ClassDesc.ofInternalName(className), clb -> {
658             clb.withFlags(ACC_PRIVATE | ACC_FINAL | ACC_SUPER)
659                .withSuperclass(InvokerBytecodeGenerator.INVOKER_SUPER_DESC)
660                .with(RuntimeVisibleAnnotationsAttribute.of(AOT_SAFE_ANNOTATION))
661                .with(SourceFileAttribute.of(className.substring(className.lastIndexOf('/') + 1)));
662             for (int i = 0; i < forms.length; i++) {
663                 new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType()).addMethod(clb, false);
664             }
665         });
666     }
667 
668     private static LambdaForm makeReinvokerFor(MethodType type) {
669         MethodHandle emptyHandle = MethodHandles.empty(type);
670         return DelegatingMethodHandle.makeReinvokerForm(emptyHandle,
671                 MethodTypeForm.LF_REBIND,
672                 BoundMethodHandle.speciesData_L(),
673                 BoundMethodHandle.speciesData_L().getterFunction(0));
674     }
675 
676     private static LambdaForm makeDelegateFor(MethodType type) {
677         MethodHandle handle = MethodHandles.empty(type);
678         return DelegatingMethodHandle.makeReinvokerForm(
679                 handle,
680                 MethodTypeForm.LF_DELEGATE,
681                 DelegatingMethodHandle.class,
682                 DelegatingMethodHandle.NF_getTarget);
683     }
684 
685     /**
686      * Returns a {@code byte[]} representation of {@code BoundMethodHandle}
687      * species class implementing the signature defined by {@code types}.
688      */
689     @SuppressWarnings({"rawtypes", "unchecked"})
690     static Map.Entry<String, byte[]> generateConcreteBMHClassBytes(final String types) {
691         for (char c : types.toCharArray()) {
692             if (!isArgBasicTypeChar(c)) {
693                 throw new IllegalArgumentException("All characters must "
694                         + "correspond to a basic field type: LIJFD");
695             }
696         }
697         final BoundMethodHandle.SpeciesData species = BoundMethodHandle.SPECIALIZER.findSpecies(types);
698         final String className = species.speciesCode().getName();
699         final ClassSpecializer.Factory factory = BoundMethodHandle.SPECIALIZER.factory();
700         final byte[] code = factory.generateConcreteSpeciesCodeFile(className, species);
701         return Map.entry(className.replace('.', '/'), code);
702     }
703 
704 }