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