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 }