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