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