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