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