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