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