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