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