1 /* 2 * Copyright (c) 2012, 2023, 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.VerifyAccess; 29 import sun.invoke.util.VerifyType; 30 import sun.invoke.util.Wrapper; 31 32 import java.lang.classfile.*; 33 import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; 34 import java.lang.classfile.attribute.SourceFileAttribute; 35 import java.lang.classfile.instruction.SwitchCase; 36 import java.lang.constant.ClassDesc; 37 import java.lang.constant.ConstantDesc; 38 import java.lang.constant.MethodTypeDesc; 39 import java.lang.invoke.LambdaForm.BasicType; 40 import java.lang.invoke.LambdaForm.Name; 41 import java.lang.invoke.LambdaForm.NamedFunction; 42 import java.lang.reflect.Modifier; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.HashMap; 46 import java.util.List; 47 import java.util.Set; 48 import java.util.function.Consumer; 49 import java.util.stream.Stream; 50 import jdk.internal.constant.MethodTypeDescImpl; 51 import jdk.internal.constant.ReferenceClassDescImpl; 52 53 import static java.lang.classfile.ClassFile.*; 54 import static java.lang.constant.ConstantDescs.*; 55 import static java.lang.invoke.LambdaForm.*; 56 import static java.lang.invoke.LambdaForm.BasicType.*; 57 import static java.lang.invoke.MethodHandleNatives.Constants.*; 58 import static java.lang.invoke.MethodHandleStatics.*; 59 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; 60 61 /** 62 * Code generation backend for LambdaForm. 63 * <p> 64 * @author John Rose, JSR 292 EG 65 */ 66 class InvokerBytecodeGenerator { 67 /** Define class names for convenience. */ 68 private static final ClassDesc CD_CasesHolder = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MethodHandleImpl$CasesHolder;"); 69 private static final ClassDesc CD_DirectMethodHandle = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/DirectMethodHandle;"); 70 private static final ClassDesc CD_MethodHandleImpl = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MethodHandleImpl;"); 71 private static final ClassDesc CD_LambdaForm = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm;"); 72 private static final ClassDesc CD_LambdaForm_Name = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm$Name;"); 73 private static final ClassDesc CD_LoopClauses = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MethodHandleImpl$LoopClauses;"); 74 private static final ClassDesc CD_Object_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/Object;"); 75 private static final ClassDesc CD_MethodHandle_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/invoke/MethodHandle;"); 76 private static final ClassDesc CD_MethodHandle_array2 = ReferenceClassDescImpl.ofValidated("[[Ljava/lang/invoke/MethodHandle;"); 77 78 private static final MethodTypeDesc MTD_boolean_Object = MethodTypeDescImpl.ofValidated(CD_boolean, CD_Object); 79 private static final MethodTypeDesc MTD_Object_int = MethodTypeDescImpl.ofValidated(CD_Object, CD_int); 80 private static final MethodTypeDesc MTD_Object_Class = MethodTypeDescImpl.ofValidated(CD_Object, CD_Class); 81 private static final MethodTypeDesc MTD_Object_Object = MethodTypeDescImpl.ofValidated(CD_Object, CD_Object); 82 83 private static final String CLASS_PREFIX = "java/lang/invoke/LambdaForm$"; 84 private static final String SOURCE_PREFIX = "LambdaForm$"; 85 86 // Static builders to avoid lambdas 87 private static final Consumer<FieldBuilder> STATIC_FINAL_FIELD = new Consumer<FieldBuilder>() { 88 @Override 89 public void accept(FieldBuilder fb) { 90 fb.withFlags(ACC_STATIC | ACC_FINAL); 91 } 92 }; 93 94 record MethodBody(Consumer<CodeBuilder> code) implements Consumer<MethodBuilder> { 95 @Override 96 public void accept(MethodBuilder mb) { 97 mb.withCode(code); 98 } 99 }; 100 101 /** Name of its super class*/ 102 static final ClassDesc INVOKER_SUPER_DESC = CD_Object; 103 104 /** Name of new class */ 105 private final String name; 106 private final String className; 107 private final ClassDesc classDesc; 108 109 private final LambdaForm lambdaForm; 110 private final String invokerName; 111 private final MethodType invokerType; 112 113 /** Info about local variables in compiled lambda form */ 114 private int[] localsMap; // index 115 private Class<?>[] localClasses; // type 116 117 private final List<ClassData> classData = new ArrayList<>(); 118 119 private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory(); 120 private static final Class<?> HOST_CLASS = LambdaForm.class; 121 private static final MethodHandles.Lookup LOOKUP = lookup(); 122 123 private static MethodHandles.Lookup lookup() { 124 try { 125 return MethodHandles.privateLookupIn(HOST_CLASS, IMPL_LOOKUP); 126 } catch (IllegalAccessException e) { 127 throw newInternalError(e); 128 } 129 } 130 131 /** Main constructor; other constructors delegate to this one. */ 132 private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize, 133 String name, String invokerName, MethodType invokerType) { 134 int p = invokerName.indexOf('.'); 135 if (p > -1) { 136 name = invokerName.substring(0, p); 137 invokerName = invokerName.substring(p + 1); 138 } 139 if (dumper().isEnabled()) { 140 name = makeDumpableClassName(name); 141 } 142 this.name = name; 143 this.className = CLASS_PREFIX + name; 144 this.classDesc = ClassDesc.ofInternalName(className); 145 this.lambdaForm = lambdaForm; 146 this.invokerName = invokerName; 147 this.invokerType = invokerType; 148 this.localsMap = new int[localsMapSize+1]; // last entry of localsMap is count of allocated local slots 149 this.localClasses = new Class<?>[localsMapSize+1]; 150 } 151 152 /** For generating LambdaForm interpreter entry points. */ 153 private InvokerBytecodeGenerator(String name, String invokerName, MethodType invokerType) { 154 this(null, invokerType.parameterCount(), 155 name, invokerName, invokerType); 156 MethodType mt = invokerType.erase(); 157 // Create an array to map name indexes to locals indexes. 158 localsMap[0] = 0; // localsMap has at least one element 159 for (int i = 1, index = 0; i < localsMap.length; i++) { 160 Wrapper w = Wrapper.forBasicType(mt.parameterType(i - 1)); 161 index += w.stackSlots(); 162 localsMap[i] = index; 163 } 164 } 165 166 /** For generating customized code for a single LambdaForm. */ 167 private InvokerBytecodeGenerator(String name, LambdaForm form, MethodType invokerType) { 168 this(name, form.lambdaName(), form, invokerType); 169 } 170 171 /** For generating customized code for a single LambdaForm. */ 172 InvokerBytecodeGenerator(String name, String invokerName, 173 LambdaForm form, MethodType invokerType) { 174 this(form, form.names.length, 175 name, invokerName, invokerType); 176 // Create an array to map name indexes to locals indexes. 177 Name[] names = form.names; 178 for (int i = 0, index = 0; i < localsMap.length; i++) { 179 localsMap[i] = index; 180 if (i < names.length) { 181 BasicType type = names[i].type(); 182 index += type.basicTypeSlots(); 183 } 184 } 185 } 186 187 /** instance counters for dumped classes */ 188 private static final HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS = 189 dumper().isEnabled() ? new HashMap<>(): null; 190 191 private static String makeDumpableClassName(String className) { 192 Integer ctr; 193 synchronized (DUMP_CLASS_FILES_COUNTERS) { 194 ctr = DUMP_CLASS_FILES_COUNTERS.get(className); 195 if (ctr == null) ctr = 0; 196 DUMP_CLASS_FILES_COUNTERS.put(className, ctr+1); 197 } 198 String sfx = ctr.toString(); 199 while (sfx.length() < 3) 200 sfx = "0" + sfx; 201 className += sfx; 202 return className; 203 } 204 205 static class ClassData { 206 final String name; 207 final ClassDesc desc; 208 final Object value; 209 210 ClassData(String name, ClassDesc desc, Object value) { 211 this.name = name; 212 this.desc = desc; 213 this.value = value; 214 } 215 216 public String name() { return name; } 217 public String toString() { 218 return name + ",value="+value; 219 } 220 } 221 222 String classData(Object arg) { 223 ClassDesc desc; 224 if (arg instanceof Class) { 225 desc = CD_Class; 226 } else if (arg instanceof MethodHandle) { 227 desc = CD_MethodHandle; 228 } else if (arg instanceof LambdaForm) { 229 desc = CD_LambdaForm; 230 } else { 231 desc = CD_Object; 232 } 233 234 // unique static variable name 235 String name; 236 if (dumper().isEnabled()) { 237 Class<?> c = arg.getClass(); 238 while (c.isArray()) { 239 c = c.getComponentType(); 240 } 241 name = "_DATA_" + c.getSimpleName() + "_" + classData.size(); 242 } else { 243 name = "_D_" + classData.size(); 244 } 245 ClassData cd = new ClassData(name, desc, arg); 246 classData.add(cd); 247 return name; 248 } 249 250 /** 251 * Extract the MemberName of a newly-defined method. 252 */ 253 private MemberName loadMethod(byte[] classFile) { 254 Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile, Set.of(), dumper()) 255 .defineClass(true, classDataValues()); 256 return resolveInvokerMember(invokerClass, invokerName, invokerType); 257 } 258 259 private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) { 260 MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic); 261 try { 262 member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, 263 HOST_CLASS, LM_TRUSTED, 264 ReflectiveOperationException.class); 265 } catch (ReflectiveOperationException e) { 266 throw newInternalError(e); 267 } 268 return member; 269 } 270 271 /** 272 * Set up class file generation. 273 */ 274 private byte[] classFileSetup(Consumer<? super ClassBuilder> config) { 275 try { 276 return ClassFile.of().build(classDesc, new Consumer<>() { 277 @Override 278 public void accept(ClassBuilder clb) { 279 clb.withFlags(ACC_FINAL | ACC_SUPER) 280 .withSuperclass(INVOKER_SUPER_DESC) 281 .with(SourceFileAttribute.of(clb.constantPool().utf8Entry(SOURCE_PREFIX + name))); 282 config.accept(clb); 283 } 284 }); 285 } catch (RuntimeException e) { 286 throw new BytecodeGenerationException(e); 287 } 288 } 289 290 private void methodSetup(ClassBuilder clb, Consumer<? super MethodBuilder> config) { 291 var invokerDesc = methodDesc(invokerType); 292 clb.withMethod(invokerName, invokerDesc, ACC_STATIC, config); 293 } 294 295 /** 296 * Returns the class data object that will be passed to `Lookup.defineHiddenClassWithClassData`. 297 * The classData is loaded in the <clinit> method of the generated class. 298 * If the class data contains only one single object, this method returns that single object. 299 * If the class data contains more than one objects, this method returns a List. 300 * 301 * This method returns null if no class data. 302 */ 303 private Object classDataValues() { 304 final List<ClassData> cd = classData; 305 return switch (cd.size()) { 306 case 0 -> null; // special case (classData is not used by <clinit>) 307 case 1 -> cd.get(0).value; // special case (single object) 308 case 2 -> List.of(cd.get(0).value, cd.get(1).value); 309 case 3 -> List.of(cd.get(0).value, cd.get(1).value, cd.get(2).value); 310 case 4 -> List.of(cd.get(0).value, cd.get(1).value, cd.get(2).value, cd.get(3).value); 311 default -> { 312 Object[] data = new Object[classData.size()]; 313 for (int i = 0; i < classData.size(); i++) { 314 data[i] = classData.get(i).value; 315 } 316 yield List.of(data); 317 } 318 }; 319 } 320 321 /* 322 * <clinit> to initialize the static final fields with the live class data 323 * LambdaForms can't use condy due to bootstrapping issue. 324 */ 325 static void clinit(ClassBuilder clb, ClassDesc classDesc, List<ClassData> classData) { 326 if (classData.isEmpty()) 327 return; 328 329 for (ClassData p : classData) { 330 // add the static field 331 clb.withField(p.name, p.desc, STATIC_FINAL_FIELD); 332 } 333 334 clb.withMethod(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new MethodBody(new Consumer<CodeBuilder>() { 335 @Override 336 public void accept(CodeBuilder cob) { 337 cob.loadConstant(classDesc) 338 .invokestatic(CD_MethodHandles, "classData", MTD_Object_Class); 339 if (classData.size() == 1) { 340 ClassData p = classData.get(0); 341 cob.checkcast(p.desc) 342 .putstatic(classDesc, p.name, p.desc); 343 } else { 344 cob.checkcast(CD_List) 345 .astore(0); 346 int index = 0; 347 var listGet = cob.constantPool().interfaceMethodRefEntry(CD_List, "get", MTD_Object_int); 348 for (ClassData p : classData) { 349 // initialize the static field 350 cob.aload(0) 351 .loadConstant(index++) 352 .invokeinterface(listGet) 353 .checkcast(p.desc) 354 .putstatic(classDesc, p.name, p.desc); 355 } 356 } 357 cob.return_(); 358 } 359 })); 360 } 361 362 private void emitLoadInsn(CodeBuilder cob, TypeKind type, int index) { 363 cob.loadLocal(type, localsMap[index]); 364 } 365 366 private void emitStoreInsn(CodeBuilder cob, TypeKind type, int index) { 367 cob.storeLocal(type, localsMap[index]); 368 } 369 370 /** 371 * Emit a boxing call. 372 * 373 * @param wrapper primitive type class to box. 374 */ 375 private void emitBoxing(CodeBuilder cob, TypeKind tk) { 376 TypeConvertingMethodAdapter.box(cob, tk); 377 } 378 379 /** 380 * Emit an unboxing call (plus preceding checkcast). 381 * 382 * @param wrapper wrapper type class to unbox. 383 */ 384 private void emitUnboxing(CodeBuilder cob, TypeKind target) { 385 switch (target) { 386 case BooleanType -> emitReferenceCast(cob, Boolean.class, null); 387 case CharType -> emitReferenceCast(cob, Character.class, null); 388 case ByteType, DoubleType, FloatType, IntType, LongType, ShortType -> 389 emitReferenceCast(cob, Number.class, null); 390 default -> {} 391 } 392 TypeConvertingMethodAdapter.unbox(cob, target); 393 } 394 395 /** 396 * Emit an implicit conversion for an argument which must be of the given pclass. 397 * This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface. 398 * 399 * @param ptype type of value present on stack 400 * @param pclass type of value required on stack 401 * @param arg compile-time representation of value on stack (Node, constant) or null if none 402 */ 403 private void emitImplicitConversion(CodeBuilder cob, BasicType ptype, Class<?> pclass, Object arg) { 404 assert(basicType(pclass) == ptype); // boxing/unboxing handled by caller 405 if (pclass == ptype.basicTypeClass() && ptype != L_TYPE) 406 return; // nothing to do 407 switch (ptype) { 408 case L_TYPE: 409 if (VerifyType.isNullConversion(Object.class, pclass, false)) { 410 if (PROFILE_LEVEL > 0) 411 emitReferenceCast(cob, Object.class, arg); 412 return; 413 } 414 emitReferenceCast(cob, pclass, arg); 415 return; 416 case I_TYPE: 417 if (!VerifyType.isNullConversion(int.class, pclass, false)) 418 emitPrimCast(cob, ptype.basicTypeKind(), TypeKind.from(pclass)); 419 return; 420 } 421 throw newInternalError("bad implicit conversion: tc="+ptype+": "+pclass); 422 } 423 424 /** Update localClasses type map. Return true if the information is already present. */ 425 private boolean assertStaticType(Class<?> cls, Name n) { 426 int local = n.index(); 427 Class<?> aclass = localClasses[local]; 428 if (aclass != null && (aclass == cls || cls.isAssignableFrom(aclass))) { 429 return true; // type info is already present 430 } else if (aclass == null || aclass.isAssignableFrom(cls)) { 431 localClasses[local] = cls; // type info can be improved 432 } 433 return false; 434 } 435 436 private void emitReferenceCast(CodeBuilder cob, Class<?> cls, Object arg) { 437 Name writeBack = null; // local to write back result 438 if (arg instanceof Name n) { 439 if (lambdaForm.useCount(n) > 1) { 440 // This guy gets used more than once. 441 writeBack = n; 442 if (assertStaticType(cls, n)) { 443 return; // this cast was already performed 444 } 445 } 446 } 447 if (isStaticallyNameable(cls)) { 448 ClassDesc sig = classDesc(cls); 449 cob.checkcast(sig); 450 } else { 451 cob.getstatic(classDesc, classData(cls), CD_Class) 452 .swap() 453 .invokevirtual(CD_Class, "cast", MTD_Object_Object); 454 if (Object[].class.isAssignableFrom(cls)) 455 cob.checkcast(CD_Object_array); 456 else if (PROFILE_LEVEL > 0) 457 cob.checkcast(CD_Object); 458 } 459 if (writeBack != null) { 460 cob.dup(); 461 emitStoreInsn(cob, TypeKind.ReferenceType, writeBack.index()); 462 } 463 } 464 465 private static MemberName resolveFrom(String name, MethodType type, Class<?> holder) { 466 assert(!UNSAFE.shouldBeInitialized(holder)) : holder + "not initialized"; 467 MemberName member = new MemberName(holder, name, type, REF_invokeStatic); 468 MemberName resolvedMember = MemberName.getFactory().resolveOrNull(REF_invokeStatic, member, holder, LM_TRUSTED); 469 traceLambdaForm(name, type, holder, resolvedMember); 470 return resolvedMember; 471 } 472 473 private static MemberName lookupPregenerated(LambdaForm form, MethodType invokerType) { 474 if (form.customized != null) { 475 // No pre-generated version for customized LF 476 return null; 477 } 478 String name = form.kind.methodName; 479 switch (form.kind) { 480 case BOUND_REINVOKER: { 481 name = name + "_" + BoundMethodHandle.speciesDataFor(form).key(); 482 return resolveFrom(name, invokerType, DelegatingMethodHandle.Holder.class); 483 } 484 case DELEGATE: return resolveFrom(name, invokerType, DelegatingMethodHandle.Holder.class); 485 case ZERO: // fall-through 486 case IDENTITY: { 487 name = name + "_" + form.returnType().basicTypeChar(); 488 return resolveFrom(name, invokerType, LambdaForm.Holder.class); 489 } 490 case EXACT_INVOKER: // fall-through 491 case EXACT_LINKER: // fall-through 492 case LINK_TO_CALL_SITE: // fall-through 493 case LINK_TO_TARGET_METHOD: // fall-through 494 case GENERIC_INVOKER: // fall-through 495 case GENERIC_LINKER: return resolveFrom(name, invokerType, Invokers.Holder.class); 496 case GET_REFERENCE: // fall-through 497 case GET_BOOLEAN: // fall-through 498 case GET_BYTE: // fall-through 499 case GET_CHAR: // fall-through 500 case GET_SHORT: // fall-through 501 case GET_INT: // fall-through 502 case GET_LONG: // fall-through 503 case GET_FLOAT: // fall-through 504 case GET_DOUBLE: // fall-through 505 case PUT_REFERENCE: // fall-through 506 case PUT_BOOLEAN: // fall-through 507 case PUT_BYTE: // fall-through 508 case PUT_CHAR: // fall-through 509 case PUT_SHORT: // fall-through 510 case PUT_INT: // fall-through 511 case PUT_LONG: // fall-through 512 case PUT_FLOAT: // fall-through 513 case PUT_DOUBLE: // fall-through 514 case DIRECT_NEW_INVOKE_SPECIAL: // fall-through 515 case DIRECT_INVOKE_INTERFACE: // fall-through 516 case DIRECT_INVOKE_SPECIAL: // fall-through 517 case DIRECT_INVOKE_SPECIAL_IFC: // fall-through 518 case DIRECT_INVOKE_STATIC: // fall-through 519 case DIRECT_INVOKE_STATIC_INIT: // fall-through 520 case DIRECT_INVOKE_VIRTUAL: return resolveFrom(name, invokerType, DirectMethodHandle.Holder.class); 521 } 522 return null; 523 } 524 525 /** 526 * Generate customized bytecode for a given LambdaForm. 527 */ 528 static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) { 529 MemberName pregenerated = lookupPregenerated(form, invokerType); 530 if (pregenerated != null) return pregenerated; // pre-generated bytecode 531 532 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType); 533 return g.loadMethod(g.generateCustomizedCodeBytes()); 534 } 535 536 /** Generates code to check that actual receiver and LambdaForm matches */ 537 private boolean checkActualReceiver(CodeBuilder cob) { 538 // Expects MethodHandle on the stack and actual receiver MethodHandle in slot #0 539 cob.dup() 540 .aload(0) 541 .invokestatic(CD_MethodHandleImpl, "assertSame", MethodTypeDescImpl.ofValidated(CD_void, CD_Object, CD_Object)); 542 return true; 543 } 544 545 static final Annotation DONTINLINE = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljdk/internal/vm/annotation/DontInline;")); 546 static final Annotation FORCEINLINE = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljdk/internal/vm/annotation/ForceInline;")); 547 static final Annotation HIDDEN = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljdk/internal/vm/annotation/Hidden;")); 548 static final Annotation INJECTEDPROFILE = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/InjectedProfile;")); 549 static final Annotation LF_COMPILED = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm$Compiled;")); 550 551 /** 552 * Generate an invoker method for the passed {@link LambdaForm}. 553 */ 554 private byte[] generateCustomizedCodeBytes() { 555 final byte[] classFile = classFileSetup(new Consumer<ClassBuilder>() { 556 @Override 557 public void accept(ClassBuilder clb) { 558 addMethod(clb); 559 clinit(clb, classDesc, classData); 560 bogusMethod(clb, lambdaForm); 561 } 562 }); 563 return classFile; 564 } 565 566 void addMethod(ClassBuilder clb) { 567 methodSetup(clb, new Consumer<MethodBuilder>() { 568 @Override 569 public void accept(MethodBuilder mb) { 570 571 List<Annotation> annotations = new ArrayList<>(3); 572 573 // Suppress this method in backtraces displayed to the user. 574 annotations.add(HIDDEN); 575 576 // Mark this method as a compiled LambdaForm 577 annotations.add(LF_COMPILED); 578 579 if (lambdaForm.forceInline) { 580 // Force inlining of this invoker method. 581 annotations.add(FORCEINLINE); 582 } else { 583 annotations.add(DONTINLINE); 584 } 585 mb.accept(RuntimeVisibleAnnotationsAttribute.of(annotations)); 586 587 classData(lambdaForm); // keep LambdaForm instance & its compiled form lifetime tightly coupled. 588 589 mb.withCode(new Consumer<CodeBuilder>() { 590 @Override 591 public void accept(CodeBuilder cob) { 592 if (lambdaForm.customized != null) { 593 // Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute 594 // receiver MethodHandle (at slot #0) with an embedded constant and use it instead. 595 // It enables more efficient code generation in some situations, since embedded constants 596 // are compile-time constants for JIT compiler. 597 cob.getstatic(classDesc, classData(lambdaForm.customized), CD_MethodHandle) 598 .checkcast(CD_MethodHandle); 599 assert(checkActualReceiver(cob)); // expects MethodHandle on top of the stack 600 cob.astore(0); 601 } 602 603 // iterate over the form's names, generating bytecode instructions for each 604 // start iterating at the first name following the arguments 605 Name onStack = null; 606 for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { 607 Name name = lambdaForm.names[i]; 608 609 emitStoreResult(cob, onStack); 610 onStack = name; // unless otherwise modified below 611 MethodHandleImpl.Intrinsic intr = name.function.intrinsicName(); 612 switch (intr) { 613 case SELECT_ALTERNATIVE: 614 assert lambdaForm.isSelectAlternative(i); 615 if (PROFILE_GWT) { 616 assert(name.arguments[0] instanceof Name n && 617 n.refersTo(MethodHandleImpl.class, "profileBoolean")); 618 mb.with(RuntimeVisibleAnnotationsAttribute.of(List.of(INJECTEDPROFILE))); 619 } 620 onStack = emitSelectAlternative(cob, name, lambdaForm.names[i+1]); 621 i++; // skip MH.invokeBasic of the selectAlternative result 622 continue; 623 case GUARD_WITH_CATCH: 624 assert lambdaForm.isGuardWithCatch(i); 625 onStack = emitGuardWithCatch(cob, i); 626 i += 2; // jump to the end of GWC idiom 627 continue; 628 case TRY_FINALLY: 629 assert lambdaForm.isTryFinally(i); 630 onStack = emitTryFinally(cob, i); 631 i += 2; // jump to the end of the TF idiom 632 continue; 633 case TABLE_SWITCH: 634 assert lambdaForm.isTableSwitch(i); 635 int numCases = (Integer) name.function.intrinsicData(); 636 onStack = emitTableSwitch(cob, i, numCases); 637 i += 2; // jump to the end of the TS idiom 638 continue; 639 case LOOP: 640 assert lambdaForm.isLoop(i); 641 onStack = emitLoop(cob, i); 642 i += 2; // jump to the end of the LOOP idiom 643 continue; 644 case ARRAY_LOAD: 645 emitArrayLoad(cob, name); 646 continue; 647 case ARRAY_STORE: 648 emitArrayStore(cob, name); 649 continue; 650 case ARRAY_LENGTH: 651 emitArrayLength(cob, name); 652 continue; 653 case IDENTITY: 654 assert(name.arguments.length == 1); 655 emitPushArguments(cob, name, 0); 656 continue; 657 case ZERO: 658 assert(name.arguments.length == 0); 659 cob.loadConstant((ConstantDesc)name.type.basicTypeWrapper().zero()); 660 continue; 661 case NONE: 662 // no intrinsic associated 663 break; 664 default: 665 throw newInternalError("Unknown intrinsic: "+intr); 666 } 667 668 MemberName member = name.function.member(); 669 if (isStaticallyInvocable(member)) { 670 emitStaticInvoke(cob, member, name); 671 } else { 672 emitInvoke(cob, name); 673 } 674 } 675 676 // return statement 677 emitReturn(cob, onStack); 678 } 679 }); 680 } 681 }); 682 } 683 684 /** 685 * The BytecodeGenerationException. 686 */ 687 @SuppressWarnings("serial") 688 static final class BytecodeGenerationException extends RuntimeException { 689 BytecodeGenerationException(Exception cause) { 690 super(cause); 691 } 692 } 693 694 void emitArrayLoad(CodeBuilder cob, Name name) { 695 Class<?> elementType = name.function.methodType().parameterType(0).getComponentType(); 696 assert elementType != null; 697 emitPushArguments(cob, name, 0); 698 if (elementType.isPrimitive()) { 699 cob.arrayLoad(TypeKind.from(elementType)); 700 } else { 701 cob.aaload(); 702 } 703 } 704 705 void emitArrayStore(CodeBuilder cob, Name name) { 706 Class<?> elementType = name.function.methodType().parameterType(0).getComponentType(); 707 assert elementType != null; 708 emitPushArguments(cob, name, 0); 709 if (elementType.isPrimitive()) { 710 cob.arrayStore(TypeKind.from(elementType)); 711 } else { 712 cob.aastore(); 713 } 714 } 715 716 void emitArrayLength(CodeBuilder cob, Name name) { 717 assert name.function.methodType().parameterType(0).isArray(); 718 emitPushArguments(cob, name, 0); 719 cob.arraylength(); 720 } 721 722 /** 723 * Emit an invoke for the given name. 724 */ 725 void emitInvoke(CodeBuilder cob, Name name) { 726 assert(!name.isLinkerMethodInvoke()); // should use the static path for these 727 if (true) { 728 // push receiver 729 MethodHandle target = name.function.resolvedHandle(); 730 assert(target != null) : name.exprString(); 731 cob.getstatic(classDesc, classData(target), CD_MethodHandle); 732 emitReferenceCast(cob, MethodHandle.class, target); 733 } else { 734 // load receiver 735 cob.aload(0); 736 emitReferenceCast(cob, MethodHandle.class, null); 737 cob.getfield(CD_MethodHandle, "form", CD_LambdaForm) 738 .getfield(CD_LambdaForm, "names", CD_LambdaForm_Name); 739 // TODO more to come 740 } 741 742 // push arguments 743 emitPushArguments(cob, name, 0); 744 745 // invocation 746 MethodType type = name.function.methodType(); 747 cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())); 748 } 749 750 private static final Class<?>[] STATICALLY_INVOCABLE_PACKAGES = { 751 // Sample classes from each package we are willing to bind to statically: 752 java.lang.Object.class, 753 java.util.Arrays.class, 754 jdk.internal.misc.Unsafe.class 755 //MethodHandle.class already covered 756 }; 757 758 static boolean isStaticallyInvocable(NamedFunction ... functions) { 759 for (NamedFunction nf : functions) { 760 if (!isStaticallyInvocable(nf.member())) { 761 return false; 762 } 763 } 764 return true; 765 } 766 767 static boolean isStaticallyInvocable(Name name) { 768 return isStaticallyInvocable(name.function.member()); 769 } 770 771 static boolean isStaticallyInvocable(MemberName member) { 772 if (member == null) return false; 773 if (member.isConstructor()) return false; 774 Class<?> cls = member.getDeclaringClass(); 775 // Fast-path non-private members declared by MethodHandles, which is a common 776 // case 777 if (MethodHandle.class.isAssignableFrom(cls) && !member.isPrivate()) { 778 assert(isStaticallyInvocableType(member.getMethodOrFieldType())); 779 return true; 780 } 781 if (cls.isArray() || cls.isPrimitive()) 782 return false; // FIXME 783 if (cls.isAnonymousClass() || cls.isLocalClass()) 784 return false; // inner class of some sort 785 if (cls.getClassLoader() != MethodHandle.class.getClassLoader()) 786 return false; // not on BCP 787 if (cls.isHidden()) 788 return false; 789 if (!isStaticallyInvocableType(member.getMethodOrFieldType())) 790 return false; 791 if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls)) 792 return true; // in java.lang.invoke package 793 if (member.isPublic() && isStaticallyNameable(cls)) 794 return true; 795 return false; 796 } 797 798 private static boolean isStaticallyInvocableType(MethodType mtype) { 799 if (!isStaticallyNameable(mtype.returnType())) 800 return false; 801 for (Class<?> ptype : mtype.ptypes()) 802 if (!isStaticallyNameable(ptype)) 803 return false; 804 return true; 805 } 806 807 static boolean isStaticallyNameable(Class<?> cls) { 808 if (cls == Object.class) 809 return true; 810 if (MethodHandle.class.isAssignableFrom(cls)) { 811 assert(!cls.isHidden()); 812 return true; 813 } 814 while (cls.isArray()) 815 cls = cls.getComponentType(); 816 if (cls.isPrimitive()) 817 return true; // int[].class, for example 818 if (cls.isHidden()) 819 return false; 820 // could use VerifyAccess.isClassAccessible but the following is a safe approximation 821 if (cls.getClassLoader() != Object.class.getClassLoader()) 822 return false; 823 if (VerifyAccess.isSamePackage(MethodHandle.class, cls)) 824 return true; 825 if (!Modifier.isPublic(cls.getModifiers())) 826 return false; 827 for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) { 828 if (VerifyAccess.isSamePackage(pkgcls, cls)) 829 return true; 830 } 831 return false; 832 } 833 834 void emitStaticInvoke(CodeBuilder cob, Name name) { 835 emitStaticInvoke(cob, name.function.member(), name); 836 } 837 838 /** 839 * Emit an invoke for the given name, using the MemberName directly. 840 */ 841 void emitStaticInvoke(CodeBuilder cob, MemberName member, Name name) { 842 assert(member.equals(name.function.member())); 843 Class<?> defc = member.getDeclaringClass(); 844 ClassDesc cdesc = classDesc(defc); 845 String mname = member.getName(); 846 byte refKind = member.getReferenceKind(); 847 if (refKind == REF_invokeSpecial) { 848 // in order to pass the verifier, we need to convert this to invokevirtual in all cases 849 assert(member.canBeStaticallyBound()) : member; 850 refKind = REF_invokeVirtual; 851 } 852 853 assert(!(member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual)); 854 855 // push arguments 856 emitPushArguments(cob, name, 0); 857 858 // invocation 859 if (member.isMethod()) { 860 var methodTypeDesc = methodDesc(member.getMethodType()); 861 cob.invoke(refKindOpcode(refKind), cdesc, mname, methodTypeDesc, 862 member.getDeclaringClass().isInterface()); 863 } else { 864 var fieldTypeDesc = classDesc(member.getFieldType()); 865 cob.fieldAccess(refKindOpcode(refKind), cdesc, mname, fieldTypeDesc); 866 } 867 // Issue a type assertion for the result, so we can avoid casts later. 868 if (name.type == L_TYPE) { 869 Class<?> rtype = member.getInvocationType().returnType(); 870 assert(!rtype.isPrimitive()); 871 if (rtype != Object.class && !rtype.isInterface()) { 872 assertStaticType(rtype, name); 873 } 874 } 875 } 876 877 Opcode refKindOpcode(byte refKind) { 878 switch (refKind) { 879 case REF_invokeVirtual: return Opcode.INVOKEVIRTUAL; 880 case REF_invokeStatic: return Opcode.INVOKESTATIC; 881 case REF_invokeSpecial: return Opcode.INVOKESPECIAL; 882 case REF_invokeInterface: return Opcode.INVOKEINTERFACE; 883 case REF_getField: return Opcode.GETFIELD; 884 case REF_putField: return Opcode.PUTFIELD; 885 case REF_getStatic: return Opcode.GETSTATIC; 886 case REF_putStatic: return Opcode.PUTSTATIC; 887 } 888 throw new InternalError("refKind="+refKind); 889 } 890 891 /** 892 * Emit bytecode for the selectAlternative idiom. 893 * 894 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest): 895 * <blockquote><pre>{@code 896 * Lambda(a0:L,a1:I)=>{ 897 * t2:I=foo.test(a1:I); 898 * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int)); 899 * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I} 900 * }</pre></blockquote> 901 */ 902 private Name emitSelectAlternative(CodeBuilder cob, Name selectAlternativeName, Name invokeBasicName) { 903 assert isStaticallyInvocable(invokeBasicName); 904 905 Name receiver = (Name) invokeBasicName.arguments[0]; 906 907 Label L_fallback = cob.newLabel(); 908 Label L_done = cob.newLabel(); 909 910 // load test result 911 emitPushArgument(cob, selectAlternativeName, 0); 912 913 // if_icmpne L_fallback 914 cob.ifeq(L_fallback); 915 916 // invoke selectAlternativeName.arguments[1] 917 Class<?>[] preForkClasses = localClasses.clone(); 918 emitPushArgument(cob, selectAlternativeName, 1); // get 2nd argument of selectAlternative 919 emitStoreInsn(cob, TypeKind.ReferenceType, receiver.index()); // store the MH in the receiver slot 920 emitStaticInvoke(cob, invokeBasicName); 921 922 // goto L_done 923 cob.goto_w(L_done); 924 925 // L_fallback: 926 cob.labelBinding(L_fallback); 927 928 // invoke selectAlternativeName.arguments[2] 929 System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); 930 emitPushArgument(cob, selectAlternativeName, 2); // get 3rd argument of selectAlternative 931 emitStoreInsn(cob, TypeKind.ReferenceType, receiver.index()); // store the MH in the receiver slot 932 emitStaticInvoke(cob, invokeBasicName); 933 934 // L_done: 935 cob.labelBinding(L_done); 936 // for now do not bother to merge typestate; just reset to the dominator state 937 System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); 938 939 return invokeBasicName; // return what's on stack 940 } 941 942 /** 943 * Emit bytecode for the guardWithCatch idiom. 944 * 945 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch): 946 * <blockquote><pre>{@code 947 * guardWithCatch=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{ 948 * t8:L=MethodHandle.invokeBasic(a4:L,a6:L,a7:L); 949 * t9:L=MethodHandleImpl.guardWithCatch(a1:L,a2:L,a3:L,t8:L); 950 * t10:I=MethodHandle.invokeBasic(a5:L,t9:L);t10:I} 951 * }</pre></blockquote> 952 * 953 * It is compiled into bytecode equivalent of the following code: 954 * <blockquote><pre>{@code 955 * try { 956 * return a1.invokeBasic(a6, a7); 957 * } catch (Throwable e) { 958 * if (!a2.isInstance(e)) throw e; 959 * return a3.invokeBasic(ex, a6, a7); 960 * }}</pre></blockquote> 961 */ 962 private Name emitGuardWithCatch(CodeBuilder cob, int pos) { 963 Name args = lambdaForm.names[pos]; 964 Name invoker = lambdaForm.names[pos+1]; 965 Name result = lambdaForm.names[pos+2]; 966 967 Label L_startBlock = cob.newLabel(); 968 Label L_endBlock = cob.newLabel(); 969 Label L_handler = cob.newLabel(); 970 Label L_done = cob.newLabel(); 971 972 Class<?> returnType = result.function.resolvedHandle().type().returnType(); 973 MethodType type = args.function.resolvedHandle().type() 974 .dropParameterTypes(0,1) 975 .changeReturnType(returnType); 976 977 cob.exceptionCatch(L_startBlock, L_endBlock, L_handler, CD_Throwable); 978 979 // Normal case 980 cob.labelBinding(L_startBlock); 981 // load target 982 emitPushArgument(cob, invoker, 0); 983 emitPushArguments(cob, args, 1); // skip 1st argument: method handle 984 cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())); 985 cob.labelBinding(L_endBlock); 986 cob.goto_w(L_done); 987 988 // Exceptional case 989 cob.labelBinding(L_handler); 990 991 // Check exception's type 992 cob.dup(); 993 // load exception class 994 emitPushArgument(cob, invoker, 1); 995 cob.swap(); 996 cob.invokevirtual(CD_Class, "isInstance", MTD_boolean_Object); 997 Label L_rethrow = cob.newLabel(); 998 cob.ifeq(L_rethrow); 999 1000 // Invoke catcher 1001 // load catcher 1002 emitPushArgument(cob, invoker, 2); 1003 cob.swap(); 1004 emitPushArguments(cob, args, 1); // skip 1st argument: method handle 1005 MethodType catcherType = type.insertParameterTypes(0, Throwable.class); 1006 cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(catcherType.basicType())); 1007 cob.goto_w(L_done); 1008 1009 cob.labelBinding(L_rethrow); 1010 cob.athrow(); 1011 1012 cob.labelBinding(L_done); 1013 1014 return result; 1015 } 1016 1017 /** 1018 * Emit bytecode for the tryFinally idiom. 1019 * <p> 1020 * The pattern looks like (Cf. MethodHandleImpl.makeTryFinally): 1021 * <blockquote><pre>{@code 1022 * // a0: BMH 1023 * // a1: target, a2: cleanup 1024 * // a3: box, a4: unbox 1025 * // a5 (and following): arguments 1026 * tryFinally=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L)=>{ 1027 * t6:L=MethodHandle.invokeBasic(a3:L,a5:L); // box the arguments into an Object[] 1028 * t7:L=MethodHandleImpl.tryFinally(a1:L,a2:L,t6:L); // call the tryFinally executor 1029 * t8:L=MethodHandle.invokeBasic(a4:L,t7:L);t8:L} // unbox the result; return the result 1030 * }</pre></blockquote> 1031 * <p> 1032 * It is compiled into bytecode equivalent to the following code: 1033 * <blockquote><pre>{@code 1034 * Throwable t; 1035 * Object r; 1036 * try { 1037 * r = a1.invokeBasic(a5); 1038 * } catch (Throwable thrown) { 1039 * t = thrown; 1040 * throw t; 1041 * } finally { 1042 * r = a2.invokeBasic(t, r, a5); 1043 * } 1044 * return r; 1045 * }</pre></blockquote> 1046 * <p> 1047 * Specifically, the bytecode will have the following form (the stack effects are given for the beginnings of 1048 * blocks, and for the situations after executing the given instruction - the code will have a slightly different 1049 * shape if the return type is {@code void}): 1050 * <blockquote><pre>{@code 1051 * TRY: (--) 1052 * load target (-- target) 1053 * load args (-- args... target) 1054 * INVOKEVIRTUAL MethodHandle.invokeBasic (depends) 1055 * FINALLY_NORMAL: (-- r_2nd* r) 1056 * store returned value (--) 1057 * load cleanup (-- cleanup) 1058 * ACONST_NULL (-- t cleanup) 1059 * load returned value (-- r_2nd* r t cleanup) 1060 * load args (-- args... r_2nd* r t cleanup) 1061 * INVOKEVIRTUAL MethodHandle.invokeBasic (-- r_2nd* r) 1062 * GOTO DONE 1063 * CATCH: (-- t) 1064 * DUP (-- t t) 1065 * FINALLY_EXCEPTIONAL: (-- t t) 1066 * load cleanup (-- cleanup t t) 1067 * SWAP (-- t cleanup t) 1068 * load default for r (-- r_2nd* r t cleanup t) 1069 * load args (-- args... r_2nd* r t cleanup t) 1070 * INVOKEVIRTUAL MethodHandle.invokeBasic (-- r_2nd* r t) 1071 * POP/POP2* (-- t) 1072 * ATHROW 1073 * DONE: (-- r) 1074 * }</pre></blockquote> 1075 * * = depends on whether the return type takes up 2 stack slots. 1076 */ 1077 private Name emitTryFinally(CodeBuilder cob, int pos) { 1078 Name args = lambdaForm.names[pos]; 1079 Name invoker = lambdaForm.names[pos+1]; 1080 Name result = lambdaForm.names[pos+2]; 1081 1082 Label lFrom = cob.newLabel(); 1083 Label lTo = cob.newLabel(); 1084 Label lCatch = cob.newLabel(); 1085 Label lDone = cob.newLabel(); 1086 1087 Class<?> returnType = result.function.resolvedHandle().type().returnType(); 1088 BasicType basicReturnType = BasicType.basicType(returnType); 1089 boolean isNonVoid = returnType != void.class; 1090 1091 MethodType type = args.function.resolvedHandle().type() 1092 .dropParameterTypes(0,1) 1093 .changeReturnType(returnType); 1094 MethodType cleanupType = type.insertParameterTypes(0, Throwable.class); 1095 if (isNonVoid) { 1096 cleanupType = cleanupType.insertParameterTypes(1, returnType); 1097 } 1098 MethodTypeDesc cleanupDesc = methodDesc(cleanupType.basicType()); 1099 1100 // exception handler table 1101 cob.exceptionCatch(lFrom, lTo, lCatch, CD_Throwable); 1102 1103 // TRY: 1104 cob.labelBinding(lFrom); 1105 emitPushArgument(cob, invoker, 0); // load target 1106 emitPushArguments(cob, args, 1); // load args (skip 0: method handle) 1107 cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())); 1108 cob.labelBinding(lTo); 1109 1110 // FINALLY_NORMAL: 1111 int index = extendLocalsMap(new Class<?>[]{ returnType }); 1112 if (isNonVoid) { 1113 emitStoreInsn(cob, basicReturnType.basicTypeKind(), index); 1114 } 1115 emitPushArgument(cob, invoker, 1); // load cleanup 1116 cob.loadConstant(null); 1117 if (isNonVoid) { 1118 emitLoadInsn(cob, basicReturnType.basicTypeKind(), index); 1119 } 1120 emitPushArguments(cob, args, 1); // load args (skip 0: method handle) 1121 cob.invokevirtual(CD_MethodHandle, "invokeBasic", cleanupDesc); 1122 cob.goto_w(lDone); 1123 1124 // CATCH: 1125 cob.labelBinding(lCatch); 1126 cob.dup(); 1127 1128 // FINALLY_EXCEPTIONAL: 1129 emitPushArgument(cob, invoker, 1); // load cleanup 1130 cob.swap(); 1131 if (isNonVoid) { 1132 emitZero(cob, BasicType.basicType(returnType)); // load default for result 1133 } 1134 emitPushArguments(cob, args, 1); // load args (skip 0: method handle) 1135 cob.invokevirtual(CD_MethodHandle, "invokeBasic", cleanupDesc); 1136 if (isNonVoid) { 1137 emitPopInsn(cob, basicReturnType); 1138 } 1139 cob.athrow(); 1140 1141 // DONE: 1142 cob.labelBinding(lDone); 1143 1144 return result; 1145 } 1146 1147 private void emitPopInsn(CodeBuilder cob, BasicType type) { 1148 switch (type) { 1149 case I_TYPE, F_TYPE, L_TYPE -> cob.pop(); 1150 case J_TYPE, D_TYPE -> cob.pop2(); 1151 default -> throw new InternalError("unknown type: " + type); 1152 } 1153 } 1154 1155 private Name emitTableSwitch(CodeBuilder cob, int pos, int numCases) { 1156 Name args = lambdaForm.names[pos]; 1157 Name invoker = lambdaForm.names[pos + 1]; 1158 Name result = lambdaForm.names[pos + 2]; 1159 1160 Class<?> returnType = result.function.resolvedHandle().type().returnType(); 1161 MethodType caseType = args.function.resolvedHandle().type() 1162 .dropParameterTypes(0, 1) // drop collector 1163 .changeReturnType(returnType); 1164 MethodTypeDesc caseDescriptor = methodDesc(caseType.basicType()); 1165 1166 emitPushArgument(cob, invoker, 2); // push cases 1167 cob.getfield(CD_CasesHolder, "cases", CD_MethodHandle_array); 1168 int casesLocal = extendLocalsMap(new Class<?>[] { MethodHandle[].class }); 1169 emitStoreInsn(cob, TypeKind.ReferenceType, casesLocal); 1170 1171 Label endLabel = cob.newLabel(); 1172 Label defaultLabel = cob.newLabel(); 1173 List<SwitchCase> cases = new ArrayList<>(numCases); 1174 for (int i = 0; i < numCases; i++) { 1175 cases.add(SwitchCase.of(i, cob.newLabel())); 1176 } 1177 1178 emitPushArgument(cob, invoker, 0); // push switch input 1179 cob.tableswitch(0, numCases - 1, defaultLabel, cases); 1180 1181 cob.labelBinding(defaultLabel); 1182 emitPushArgument(cob, invoker, 1); // push default handle 1183 emitPushArguments(cob, args, 1); // again, skip collector 1184 cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor); 1185 cob.goto_(endLabel); 1186 1187 for (int i = 0; i < numCases; i++) { 1188 cob.labelBinding(cases.get(i).target()); 1189 // Load the particular case: 1190 emitLoadInsn(cob, TypeKind.ReferenceType, casesLocal); 1191 cob.loadConstant(i); 1192 cob.aaload(); 1193 1194 // invoke it: 1195 emitPushArguments(cob, args, 1); // again, skip collector 1196 cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor); 1197 1198 cob.goto_(endLabel); 1199 } 1200 1201 cob.labelBinding(endLabel); 1202 1203 return result; 1204 } 1205 1206 /** 1207 * Emit bytecode for the loop idiom. 1208 * <p> 1209 * The pattern looks like (Cf. MethodHandleImpl.loop): 1210 * <blockquote><pre>{@code 1211 * // a0: BMH 1212 * // a1: LoopClauses (containing an array of arrays: inits, steps, preds, finis) 1213 * // a2: box, a3: unbox 1214 * // a4 (and following): arguments 1215 * loop=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L)=>{ 1216 * t5:L=MethodHandle.invokeBasic(a2:L,a4:L); // box the arguments into an Object[] 1217 * t6:L=MethodHandleImpl.loop(bt:L,a1:L,t5:L); // call the loop executor (with supplied types in bt) 1218 * t7:L=MethodHandle.invokeBasic(a3:L,t6:L);t7:L} // unbox the result; return the result 1219 * }</pre></blockquote> 1220 * <p> 1221 * It is compiled into bytecode equivalent to the code seen in {@link MethodHandleImpl#loop(BasicType[], 1222 * MethodHandleImpl.LoopClauses, Object...)}, with the difference that no arrays 1223 * will be used for local state storage. Instead, the local state will be mapped to actual stack slots. 1224 * <p> 1225 * Bytecode generation applies an unrolling scheme to enable better bytecode generation regarding local state type 1226 * handling. The generated bytecode will have the following form ({@code void} types are ignored for convenience). 1227 * Assume there are {@code C} clauses in the loop. 1228 * <blockquote><pre>{@code 1229 * PREINIT: ALOAD_1 1230 * CHECKCAST LoopClauses 1231 * GETFIELD LoopClauses.clauses 1232 * ASTORE clauseDataIndex // place the clauses 2-dimensional array on the stack 1233 * INIT: (INIT_SEQ for clause 1) 1234 * ... 1235 * (INIT_SEQ for clause C) 1236 * LOOP: (LOOP_SEQ for clause 1) 1237 * ... 1238 * (LOOP_SEQ for clause C) 1239 * GOTO LOOP 1240 * DONE: ... 1241 * }</pre></blockquote> 1242 * <p> 1243 * The {@code INIT_SEQ_x} sequence for clause {@code x} (with {@code x} ranging from {@code 0} to {@code C-1}) has 1244 * the following shape. Assume slot {@code vx} is used to hold the state for clause {@code x}. 1245 * <blockquote><pre>{@code 1246 * INIT_SEQ_x: ALOAD clauseDataIndex 1247 * ICONST_0 1248 * AALOAD // load the inits array 1249 * ICONST x 1250 * AALOAD // load the init handle for clause x 1251 * load args 1252 * INVOKEVIRTUAL MethodHandle.invokeBasic 1253 * store vx 1254 * }</pre></blockquote> 1255 * <p> 1256 * The {@code LOOP_SEQ_x} sequence for clause {@code x} (with {@code x} ranging from {@code 0} to {@code C-1}) has 1257 * the following shape. Again, assume slot {@code vx} is used to hold the state for clause {@code x}. 1258 * <blockquote><pre>{@code 1259 * LOOP_SEQ_x: ALOAD clauseDataIndex 1260 * ICONST_1 1261 * AALOAD // load the steps array 1262 * ICONST x 1263 * AALOAD // load the step handle for clause x 1264 * load locals 1265 * load args 1266 * INVOKEVIRTUAL MethodHandle.invokeBasic 1267 * store vx 1268 * ALOAD clauseDataIndex 1269 * ICONST_2 1270 * AALOAD // load the preds array 1271 * ICONST x 1272 * AALOAD // load the pred handle for clause x 1273 * load locals 1274 * load args 1275 * INVOKEVIRTUAL MethodHandle.invokeBasic 1276 * IFNE LOOP_SEQ_x+1 // predicate returned false -> jump to next clause 1277 * ALOAD clauseDataIndex 1278 * ICONST_3 1279 * AALOAD // load the finis array 1280 * ICONST x 1281 * AALOAD // load the fini handle for clause x 1282 * load locals 1283 * load args 1284 * INVOKEVIRTUAL MethodHandle.invokeBasic 1285 * GOTO DONE // jump beyond end of clauses to return from loop 1286 * }</pre></blockquote> 1287 */ 1288 private Name emitLoop(CodeBuilder cob, int pos) { 1289 Name args = lambdaForm.names[pos]; 1290 Name invoker = lambdaForm.names[pos+1]; 1291 Name result = lambdaForm.names[pos+2]; 1292 1293 // extract clause and loop-local state types 1294 // find the type info in the loop invocation 1295 BasicType[] loopClauseTypes = (BasicType[]) invoker.arguments[0]; 1296 Class<?>[] loopLocalStateTypes = Stream.of(loopClauseTypes) 1297 .filter(bt -> bt != BasicType.V_TYPE) 1298 .map(BasicType::basicTypeClass).toArray(Class<?>[]::new); 1299 Class<?>[] localTypes = new Class<?>[loopLocalStateTypes.length + 1]; 1300 localTypes[0] = MethodHandleImpl.LoopClauses.class; 1301 System.arraycopy(loopLocalStateTypes, 0, localTypes, 1, loopLocalStateTypes.length); 1302 1303 final int clauseDataIndex = extendLocalsMap(localTypes); 1304 final int firstLoopStateIndex = clauseDataIndex + 1; 1305 1306 Class<?> returnType = result.function.resolvedHandle().type().returnType(); 1307 MethodType loopType = args.function.resolvedHandle().type() 1308 .dropParameterTypes(0,1) 1309 .changeReturnType(returnType); 1310 MethodType loopHandleType = loopType.insertParameterTypes(0, loopLocalStateTypes); 1311 MethodType predType = loopHandleType.changeReturnType(boolean.class); 1312 MethodType finiType = loopHandleType; 1313 1314 final int nClauses = loopClauseTypes.length; 1315 1316 // indices to invoker arguments to load method handle arrays 1317 final int inits = 1; 1318 final int steps = 2; 1319 final int preds = 3; 1320 final int finis = 4; 1321 1322 Label lLoop = cob.newLabel(); 1323 Label lDone = cob.newLabel(); 1324 Label lNext; 1325 1326 // PREINIT: 1327 emitPushArgument(cob, MethodHandleImpl.LoopClauses.class, invoker.arguments[1]); 1328 cob.getfield(CD_LoopClauses, "clauses", CD_MethodHandle_array2); 1329 emitStoreInsn(cob, TypeKind.ReferenceType, clauseDataIndex); 1330 1331 // INIT: 1332 for (int c = 0, state = 0; c < nClauses; ++c) { 1333 MethodType cInitType = loopType.changeReturnType(loopClauseTypes[c].basicTypeClass()); 1334 emitLoopHandleInvoke(cob, invoker, inits, c, args, false, cInitType, loopLocalStateTypes, clauseDataIndex, 1335 firstLoopStateIndex); 1336 if (cInitType.returnType() != void.class) { 1337 emitStoreInsn(cob, BasicType.basicType(cInitType.returnType()).basicTypeKind(), firstLoopStateIndex + state); 1338 ++state; 1339 } 1340 } 1341 1342 // LOOP: 1343 cob.labelBinding(lLoop); 1344 1345 for (int c = 0, state = 0; c < nClauses; ++c) { 1346 lNext = cob.newLabel(); 1347 1348 MethodType stepType = loopHandleType.changeReturnType(loopClauseTypes[c].basicTypeClass()); 1349 boolean isVoid = stepType.returnType() == void.class; 1350 1351 // invoke loop step 1352 emitLoopHandleInvoke(cob, invoker, steps, c, args, true, stepType, loopLocalStateTypes, clauseDataIndex, 1353 firstLoopStateIndex); 1354 if (!isVoid) { 1355 emitStoreInsn(cob, BasicType.basicType(stepType.returnType()).basicTypeKind(), firstLoopStateIndex + state); 1356 ++state; 1357 } 1358 1359 // invoke loop predicate 1360 emitLoopHandleInvoke(cob, invoker, preds, c, args, true, predType, loopLocalStateTypes, clauseDataIndex, 1361 firstLoopStateIndex); 1362 cob.ifne(lNext); 1363 1364 // invoke fini 1365 emitLoopHandleInvoke(cob, invoker, finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex, 1366 firstLoopStateIndex); 1367 cob.goto_w(lDone); 1368 1369 // this is the beginning of the next loop clause 1370 cob.labelBinding(lNext); 1371 } 1372 1373 cob.goto_w(lLoop); 1374 1375 // DONE: 1376 cob.labelBinding(lDone); 1377 1378 return result; 1379 } 1380 1381 private int extendLocalsMap(Class<?>[] types) { 1382 int firstSlot = localsMap.length - 1; 1383 localsMap = Arrays.copyOf(localsMap, localsMap.length + types.length); 1384 localClasses = Arrays.copyOf(localClasses, localClasses.length + types.length); 1385 System.arraycopy(types, 0, localClasses, firstSlot, types.length); 1386 int index = localsMap[firstSlot - 1] + 1; 1387 int lastSlots = 0; 1388 for (int i = 0; i < types.length; ++i) { 1389 localsMap[firstSlot + i] = index; 1390 lastSlots = BasicType.basicType(localClasses[firstSlot + i]).basicTypeSlots(); 1391 index += lastSlots; 1392 } 1393 localsMap[localsMap.length - 1] = index - lastSlots; 1394 return firstSlot; 1395 } 1396 1397 private void emitLoopHandleInvoke(CodeBuilder cob, Name holder, int handles, int clause, Name args, boolean pushLocalState, 1398 MethodType type, Class<?>[] loopLocalStateTypes, int clauseDataSlot, 1399 int firstLoopStateSlot) { 1400 // load handle for clause 1401 emitPushClauseArray(cob, clauseDataSlot, handles); 1402 cob.loadConstant(clause); 1403 cob.aaload(); 1404 // load loop state (preceding the other arguments) 1405 if (pushLocalState) { 1406 for (int s = 0; s < loopLocalStateTypes.length; ++s) { 1407 emitLoadInsn(cob, BasicType.basicType(loopLocalStateTypes[s]).basicTypeKind(), firstLoopStateSlot + s); 1408 } 1409 } 1410 // load loop args (skip 0: method handle) 1411 emitPushArguments(cob, args, 1); 1412 cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type)); 1413 } 1414 1415 private void emitPushClauseArray(CodeBuilder cob, int clauseDataSlot, int which) { 1416 emitLoadInsn(cob, TypeKind.ReferenceType, clauseDataSlot); 1417 cob.loadConstant(which - 1); 1418 cob.aaload(); 1419 } 1420 1421 private void emitZero(CodeBuilder cob, BasicType type) { 1422 switch (type) { 1423 case I_TYPE -> cob.iconst_0(); 1424 case J_TYPE -> cob.lconst_0(); 1425 case F_TYPE -> cob.fconst_0(); 1426 case D_TYPE -> cob.dconst_0(); 1427 case L_TYPE -> cob.aconst_null(); 1428 default -> throw new InternalError("unknown type: " + type); 1429 }; 1430 } 1431 1432 private void emitPushArguments(CodeBuilder cob, Name args, int start) { 1433 MethodType type = args.function.methodType(); 1434 for (int i = start; i < args.arguments.length; i++) { 1435 emitPushArgument(cob, type.parameterType(i), args.arguments[i]); 1436 } 1437 } 1438 1439 private void emitPushArgument(CodeBuilder cob, Name name, int paramIndex) { 1440 Object arg = name.arguments[paramIndex]; 1441 Class<?> ptype = name.function.methodType().parameterType(paramIndex); 1442 emitPushArgument(cob, ptype, arg); 1443 } 1444 1445 private void emitPushArgument(CodeBuilder cob, Class<?> ptype, Object arg) { 1446 BasicType bptype = basicType(ptype); 1447 if (arg instanceof Name n) { 1448 emitLoadInsn(cob, n.type.basicTypeKind(), n.index()); 1449 emitImplicitConversion(cob, n.type, ptype, n); 1450 } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) { 1451 cob.loadConstant((ConstantDesc)arg); 1452 } else { 1453 if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) { 1454 cob.loadConstant((ConstantDesc)arg); 1455 } else { 1456 cob.getstatic(classDesc, classData(arg), CD_Object); 1457 emitImplicitConversion(cob, L_TYPE, ptype, arg); 1458 } 1459 } 1460 } 1461 1462 /** 1463 * Store the name to its local, if necessary. 1464 */ 1465 private void emitStoreResult(CodeBuilder cob, Name name) { 1466 if (name != null && name.type != V_TYPE) { 1467 // non-void: actually assign 1468 emitStoreInsn(cob, name.type.basicTypeKind(), name.index()); 1469 } 1470 } 1471 1472 /** 1473 * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type. 1474 */ 1475 private void emitReturn(CodeBuilder cob, Name onStack) { 1476 // return statement 1477 Class<?> rclass = invokerType.returnType(); 1478 BasicType rtype = lambdaForm.returnType(); 1479 assert(rtype == basicType(rclass)); // must agree 1480 if (rtype == V_TYPE) { 1481 // void 1482 cob.return_(); 1483 // it doesn't matter what rclass is; the JVM will discard any value 1484 } else { 1485 LambdaForm.Name rn = lambdaForm.names[lambdaForm.result]; 1486 1487 // put return value on the stack if it is not already there 1488 if (rn != onStack) { 1489 emitLoadInsn(cob, rtype.basicTypeKind(), lambdaForm.result); 1490 } 1491 1492 emitImplicitConversion(cob, rtype, rclass, rn); 1493 1494 // generate actual return statement 1495 cob.return_(rtype.basicTypeKind()); 1496 } 1497 } 1498 1499 /** 1500 * Emit a type conversion bytecode casting from "from" to "to". 1501 */ 1502 private void emitPrimCast(CodeBuilder cob, TypeKind from, TypeKind to) { 1503 // Here's how. 1504 // - indicates forbidden 1505 // <-> indicates implicit 1506 // to ----> boolean byte short char int long float double 1507 // from boolean <-> - - - - - - - 1508 // byte - <-> i2s i2c <-> i2l i2f i2d 1509 // short - i2b <-> i2c <-> i2l i2f i2d 1510 // char - i2b i2s <-> <-> i2l i2f i2d 1511 // int - i2b i2s i2c <-> i2l i2f i2d 1512 // long - l2i,i2b l2i,i2s l2i,i2c l2i <-> l2f l2d 1513 // float - f2i,i2b f2i,i2s f2i,i2c f2i f2l <-> f2d 1514 // double - d2i,i2b d2i,i2s d2i,i2c d2i d2l d2f <-> 1515 if (from != to && from != TypeKind.BooleanType) try { 1516 cob.conversion(from, to); 1517 } catch (IllegalArgumentException e) { 1518 throw new IllegalStateException("unhandled prim cast: " + from + "2" + to); 1519 } 1520 } 1521 1522 /** 1523 * Generate bytecode for a LambdaForm.vmentry which calls interpretWithArguments. 1524 */ 1525 static MemberName generateLambdaFormInterpreterEntryPoint(MethodType mt) { 1526 assert(isValidSignature(basicTypeSignature(mt))); 1527 String name = "interpret_"+basicTypeChar(mt.returnType()); 1528 MethodType type = mt; // includes leading argument 1529 type = type.changeParameterType(0, MethodHandle.class); 1530 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", name, type); 1531 return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes()); 1532 } 1533 1534 private byte[] generateLambdaFormInterpreterEntryPointBytes() { 1535 final byte[] classFile = classFileSetup(new Consumer<ClassBuilder>() { 1536 @Override 1537 public void accept(ClassBuilder clb) { 1538 methodSetup(clb, new Consumer<MethodBuilder>() { 1539 @Override 1540 public void accept(MethodBuilder mb) { 1541 1542 mb.with(RuntimeVisibleAnnotationsAttribute.of(List.of( 1543 HIDDEN, // Suppress this method in backtraces displayed to the user. 1544 DONTINLINE // Don't inline the interpreter entry. 1545 ))); 1546 1547 mb.withCode(new Consumer<CodeBuilder>() { 1548 @Override 1549 public void accept(CodeBuilder cob) { 1550 // create parameter array 1551 cob.loadConstant(invokerType.parameterCount()); 1552 cob.anewarray(CD_Object); 1553 1554 // fill parameter array 1555 for (int i = 0; i < invokerType.parameterCount(); i++) { 1556 Class<?> ptype = invokerType.parameterType(i); 1557 cob.dup(); 1558 cob.loadConstant(i); 1559 emitLoadInsn(cob, basicType(ptype).basicTypeKind(), i); 1560 // box if primitive type 1561 if (ptype.isPrimitive()) { 1562 emitBoxing(cob, TypeKind.from(ptype)); 1563 } 1564 cob.aastore(); 1565 } 1566 // invoke 1567 cob.aload(0); 1568 cob.getfield(CD_MethodHandle, "form", CD_LambdaForm); 1569 cob.swap(); // swap form and array; avoid local variable 1570 cob.invokevirtual(CD_LambdaForm, "interpretWithArguments", MethodTypeDescImpl.ofValidated(CD_Object, CD_Object_array)); 1571 1572 // maybe unbox 1573 Class<?> rtype = invokerType.returnType(); 1574 TypeKind rtypeK = TypeKind.from(rtype); 1575 if (rtype.isPrimitive() && rtype != void.class) { 1576 emitUnboxing(cob, rtypeK); 1577 } 1578 1579 // return statement 1580 cob.return_(rtypeK); 1581 } 1582 }); 1583 } 1584 }); 1585 clinit(clb, classDesc, classData); 1586 bogusMethod(clb, invokerType); 1587 } 1588 }); 1589 return classFile; 1590 } 1591 1592 /** 1593 * Generate bytecode for a NamedFunction invoker. 1594 */ 1595 static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) { 1596 MethodType invokerType = NamedFunction.INVOKER_METHOD_TYPE; 1597 String invokerName = "invoke_" + shortenSignature(basicTypeSignature(typeForm.erasedType())); 1598 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType); 1599 return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm)); 1600 } 1601 1602 private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) { 1603 MethodType dstType = typeForm.erasedType(); 1604 final byte[] classFile = classFileSetup(new Consumer<ClassBuilder>() { 1605 @Override 1606 public void accept(ClassBuilder clb) { 1607 methodSetup(clb, new Consumer<MethodBuilder>() { 1608 @Override 1609 public void accept(MethodBuilder mb) { 1610 1611 mb.with(RuntimeVisibleAnnotationsAttribute.of(List.of( 1612 HIDDEN, // Suppress this method in backtraces displayed to the user. 1613 FORCEINLINE // Force inlining of this invoker method. 1614 ))); 1615 1616 mb.withCode(new Consumer<CodeBuilder>() { 1617 @Override 1618 public void accept(CodeBuilder cob) { 1619 // Load receiver 1620 cob.aload(0); 1621 1622 // Load arguments from array 1623 for (int i = 0; i < dstType.parameterCount(); i++) { 1624 cob.aload(1); 1625 cob.loadConstant(i); 1626 cob.aaload(); 1627 1628 // Maybe unbox 1629 Class<?> dptype = dstType.parameterType(i); 1630 if (dptype.isPrimitive()) { 1631 TypeKind dstTK = TypeKind.from(dptype); 1632 TypeKind srcTK = dstTK.asLoadable(); 1633 emitUnboxing(cob, srcTK); 1634 emitPrimCast(cob, srcTK, dstTK); 1635 } 1636 } 1637 1638 // Invoke 1639 MethodTypeDesc targetDesc = methodDesc(dstType.basicType()); 1640 cob.invokevirtual(CD_MethodHandle, "invokeBasic", targetDesc); 1641 1642 // Box primitive types 1643 Class<?> rtype = dstType.returnType(); 1644 if (rtype != void.class && rtype.isPrimitive()) { 1645 TypeKind srcTK = TypeKind.from(rtype); 1646 TypeKind dstTK = srcTK.asLoadable(); 1647 // boolean casts not allowed 1648 emitPrimCast(cob, srcTK, dstTK); 1649 emitBoxing(cob, dstTK); 1650 } 1651 1652 // If the return type is void we return a null reference. 1653 if (rtype == void.class) { 1654 cob.aconst_null(); 1655 } 1656 cob.areturn(); // NOTE: NamedFunction invokers always return a reference value. 1657 } 1658 }); 1659 } 1660 }); 1661 clinit(clb, classDesc, classData); 1662 bogusMethod(clb, dstType); 1663 } 1664 }); 1665 return classFile; 1666 } 1667 1668 /** 1669 * Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool 1670 * for debugging purposes. 1671 */ 1672 private void bogusMethod(ClassBuilder clb, Object os) { 1673 if (dumper().isEnabled()) { 1674 clb.withMethod("dummy", MTD_void, ACC_STATIC, new MethodBody(new Consumer<CodeBuilder>() { 1675 @Override 1676 public void accept(CodeBuilder cob) { 1677 cob.loadConstant(os.toString()); 1678 cob.pop(); 1679 cob.return_(); 1680 } 1681 })); 1682 } 1683 } 1684 1685 static ClassDesc classDesc(Class<?> cls) { 1686 // assert(VerifyAccess.isTypeVisible(cls, Object.class)) : cls.getName(); 1687 return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor() 1688 : cls == MethodHandle.class ? CD_MethodHandle 1689 : cls == DirectMethodHandle.class ? CD_DirectMethodHandle 1690 : cls == Object.class ? CD_Object 1691 : ReferenceClassDescImpl.ofValidated(cls.descriptorString()); 1692 } 1693 1694 static MethodTypeDesc methodDesc(MethodType mt) { 1695 var params = new ClassDesc[mt.parameterCount()]; 1696 for (int i = 0; i < params.length; i++) { 1697 params[i] = classDesc(mt.parameterType(i)); 1698 } 1699 return MethodTypeDescImpl.ofValidated(classDesc(mt.returnType()), params); 1700 } 1701 }