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