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 }