1 /* 2 * Copyright (c) 2016, 2024, 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 LambdaForm zero = LambdaForm.zeroForm(type); 382 String name = zero.kind.defaultLambdaName 383 + "_" + zero.returnType().basicTypeChar(); 384 if (dedupSet.add(name)) { 385 names.add(name); 386 forms.add(zero); 387 } 388 389 LambdaForm identity = LambdaForm.identityForm(type); 390 name = identity.kind.defaultLambdaName 391 + "_" + identity.returnType().basicTypeChar(); 392 if (dedupSet.add(name)) { 393 names.add(name); 394 forms.add(identity); 395 } 396 } 397 return generateCodeBytesForLFs(className, 398 names.toArray(new String[0]), 399 forms.toArray(new LambdaForm[0])); 400 } 401 402 /** 403 * Returns a {@code byte[]} representation of a class implementing 404 * DirectMethodHandle of each pairwise combination of {@code MethodType} and 405 * an {@code int} representing method type. 406 */ 407 static byte[] generateDirectMethodHandleHolderClassBytes(String className, 408 MethodType[] methodTypes, int[] types) { 409 ArrayList<LambdaForm> forms = new ArrayList<>(); 410 ArrayList<String> names = new ArrayList<>(); 411 for (int i = 0; i < methodTypes.length; i++) { 412 // invokeVirtual and invokeInterface must have a leading Object 413 // parameter, i.e., the receiver 414 if (types[i] == LF_INVVIRTUAL || types[i] == LF_INVINTERFACE) { 415 if (methodTypes[i].parameterCount() < 1 || 416 methodTypes[i].parameterType(0) != Object.class) { 417 throw new InternalError("Invalid method type for " + 418 (types[i] == LF_INVVIRTUAL ? "invokeVirtual" : "invokeInterface") + 419 " DMH, needs at least two leading reference arguments: " + 420 methodTypes[i]); 421 } 422 } 423 424 LambdaForm form = DirectMethodHandle.makePreparedLambdaForm(methodTypes[i], types[i]); 425 forms.add(form); 426 names.add(form.kind.defaultLambdaName); 427 } 428 for (Wrapper wrapper : Wrapper.values()) { 429 if (wrapper == Wrapper.VOID) { 430 continue; 431 } 432 for (byte b = DirectMethodHandle.AF_GETFIELD; b < DirectMethodHandle.AF_LIMIT; b++) { 433 int ftype = DirectMethodHandle.ftypeKind(wrapper.primitiveType()); 434 LambdaForm form = DirectMethodHandle 435 .makePreparedFieldLambdaForm(b, /*isVolatile*/false, ftype); 436 if (form.kind != LambdaForm.Kind.GENERIC) { 437 forms.add(form); 438 names.add(form.kind.defaultLambdaName); 439 } 440 // volatile 441 form = DirectMethodHandle 442 .makePreparedFieldLambdaForm(b, /*isVolatile*/true, ftype); 443 if (form.kind != LambdaForm.Kind.GENERIC) { 444 forms.add(form); 445 names.add(form.kind.defaultLambdaName); 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 * DelegatingMethodHandles of each {@code MethodType} kind in the 457 * {@code methodTypes} argument. 458 */ 459 static byte[] generateDelegatingMethodHandleHolderClassBytes(String className, 460 MethodType[] methodTypes) { 461 462 HashSet<MethodType> dedupSet = new HashSet<>(); 463 ArrayList<LambdaForm> forms = new ArrayList<>(); 464 ArrayList<String> names = new ArrayList<>(); 465 for (int i = 0; i < methodTypes.length; i++) { 466 // generate methods representing the DelegatingMethodHandle 467 if (dedupSet.add(methodTypes[i])) { 468 // reinvokers are variant with the associated SpeciesData 469 // and shape of the target LF, but we can easily pregenerate 470 // the basic reinvokers associated with Species_L. Ultimately we 471 // may want to consider pregenerating more of these, which will 472 // require an even more complex naming scheme 473 LambdaForm reinvoker = makeReinvokerFor(methodTypes[i]); 474 forms.add(reinvoker); 475 String speciesSig = BoundMethodHandle.speciesDataFor(reinvoker).key(); 476 assert(speciesSig.equals("L")); 477 names.add(reinvoker.kind.defaultLambdaName + "_" + speciesSig); 478 479 LambdaForm delegate = makeDelegateFor(methodTypes[i]); 480 forms.add(delegate); 481 names.add(delegate.kind.defaultLambdaName); 482 } 483 } 484 return generateCodeBytesForLFs(className, 485 names.toArray(new String[0]), 486 forms.toArray(new LambdaForm[0])); 487 } 488 489 /** 490 * Returns a {@code byte[]} representation of a class implementing 491 * the invoker forms for the set of supplied {@code linkerMethodTypes} 492 * {@code invokerMethodTypes}, and {@code callSiteMethodTypes}. 493 */ 494 static byte[] generateInvokersHolderClassBytes(String className, 495 MethodType[] linkerMethodTypes, MethodType[] invokerMethodTypes, 496 MethodType[] callSiteMethodTypes) { 497 498 HashSet<MethodType> dedupSet = new HashSet<>(); 499 ArrayList<LambdaForm> forms = new ArrayList<>(); 500 ArrayList<String> names = new ArrayList<>(); 501 502 int[] invokerTypes = { 503 MethodTypeForm.LF_EX_INVOKER, 504 MethodTypeForm.LF_GEN_INVOKER, 505 }; 506 507 for (MethodType methodType : invokerMethodTypes) { 508 // generate methods representing invokers of the specified type 509 if (dedupSet.add(methodType)) { 510 for (int type : invokerTypes) { 511 LambdaForm invokerForm = Invokers.invokeHandleForm(methodType, 512 /*customized*/false, type); 513 forms.add(invokerForm); 514 names.add(invokerForm.kind.defaultLambdaName); 515 } 516 } 517 } 518 519 int[] linkerTypes = { 520 MethodTypeForm.LF_EX_LINKER, 521 MethodTypeForm.LF_GEN_LINKER, 522 }; 523 524 dedupSet = new HashSet<>(); 525 for (MethodType methodType : linkerMethodTypes) { 526 // generate methods representing linkers of the specified type 527 if (dedupSet.add(methodType)) { 528 for (int type : linkerTypes) { 529 LambdaForm linkerForm = Invokers.invokeHandleForm(methodType, 530 /*customized*/false, type); 531 forms.add(linkerForm); 532 names.add(linkerForm.kind.defaultLambdaName); 533 } 534 } 535 } 536 537 dedupSet = new HashSet<>(); 538 for (int i = 0; i < callSiteMethodTypes.length; i++) { 539 // generate methods representing invokers of the specified type 540 if (dedupSet.add(callSiteMethodTypes[i])) { 541 LambdaForm callSiteForm = Invokers.callSiteForm(callSiteMethodTypes[i], true); 542 forms.add(callSiteForm); 543 names.add(callSiteForm.kind.defaultLambdaName); 544 545 LambdaForm methodHandleForm = Invokers.callSiteForm(callSiteMethodTypes[i], false); 546 forms.add(methodHandleForm); 547 names.add(methodHandleForm.kind.defaultLambdaName); 548 } 549 } 550 551 return generateCodeBytesForLFs(className, 552 names.toArray(new String[0]), 553 forms.toArray(new LambdaForm[0])); 554 } 555 556 /* 557 * Generate customized code for a set of LambdaForms of specified types into 558 * a class with a specified name. 559 */ 560 private static byte[] generateCodeBytesForLFs(String className, String[] names, LambdaForm[] forms) { 561 return ClassFile.of().build(ClassDesc.ofInternalName(className), clb -> { 562 clb.withFlags(ACC_PRIVATE | ACC_FINAL | ACC_SUPER) 563 .withSuperclass(InvokerBytecodeGenerator.INVOKER_SUPER_DESC) 564 .with(SourceFileAttribute.of(className.substring(className.lastIndexOf('/') + 1))); 565 for (int i = 0; i < forms.length; i++) { 566 new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType()).addMethod(clb, false); 567 } 568 }); 569 } 570 571 private static LambdaForm makeReinvokerFor(MethodType type) { 572 MethodHandle emptyHandle = MethodHandles.empty(type); 573 return DelegatingMethodHandle.makeReinvokerForm(emptyHandle, 574 MethodTypeForm.LF_REBIND, 575 BoundMethodHandle.speciesData_L(), 576 BoundMethodHandle.speciesData_L().getterFunction(0)); 577 } 578 579 private static LambdaForm makeDelegateFor(MethodType type) { 580 MethodHandle handle = MethodHandles.empty(type); 581 return DelegatingMethodHandle.makeReinvokerForm( 582 handle, 583 MethodTypeForm.LF_DELEGATE, 584 DelegatingMethodHandle.class, 585 DelegatingMethodHandle.NF_getTarget); 586 } 587 588 /** 589 * Returns a {@code byte[]} representation of {@code BoundMethodHandle} 590 * species class implementing the signature defined by {@code types}. 591 */ 592 @SuppressWarnings({"rawtypes", "unchecked"}) 593 static Map.Entry<String, byte[]> generateConcreteBMHClassBytes(final String types) { 594 for (char c : types.toCharArray()) { 595 if (!isArgBasicTypeChar(c)) { 596 throw new IllegalArgumentException("All characters must " 597 + "correspond to a basic field type: LIJFD"); 598 } 599 } 600 final BoundMethodHandle.SpeciesData species = BoundMethodHandle.SPECIALIZER.findSpecies(types); 601 final String className = species.speciesCode().getName(); 602 final ClassSpecializer.Factory factory = BoundMethodHandle.SPECIALIZER.factory(); 603 final byte[] code = factory.generateConcreteSpeciesCodeFile(className, species); 604 return Map.entry(className.replace('.', '/'), code); 605 } 606 607 }