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