1 /*
   2  * Copyright (c) 2012, 2013, 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 java.lang.invoke.LambdaForm.Name;
  30 
  31 import sun.invoke.util.Wrapper;
  32 
  33 import java.io.*;
  34 import java.util.*;
  35 
  36 import jdk.internal.org.objectweb.asm.*;
  37 
  38 import java.lang.reflect.*;
  39 import static java.lang.invoke.MethodHandleStatics.*;
  40 import static java.lang.invoke.MethodHandleNatives.Constants.*;
  41 import sun.invoke.util.VerifyType;
  42 
  43 /**
  44  * Code generation backend for LambdaForm.
  45  * <p>
  46  * @author John Rose, JSR 292 EG
  47  */
  48 class InvokerBytecodeGenerator {
  49     /** Define class names for convenience. */
  50     private static final String MH      = "java/lang/invoke/MethodHandle";
  51     private static final String LF      = "java/lang/invoke/LambdaForm";
  52     private static final String LFN     = "java/lang/invoke/LambdaForm$Name";
  53     private static final String CLS     = "java/lang/Class";
  54     private static final String OBJ     = "java/lang/Object";
  55     private static final String OBJARY  = "[Ljava/lang/Object;";
  56 
  57     private static final String LF_SIG  = "L" + LF + ";";
  58     private static final String LFN_SIG = "L" + LFN + ";";
  59     private static final String LL_SIG  = "(L" + OBJ + ";)L" + OBJ + ";";
  60 
  61     /** Name of its super class*/
  62     private static final String superName = LF;
  63 
  64     /** Name of new class */
  65     private final String className;
  66 
  67     /** Name of the source file (for stack trace printing). */
  68     private final String sourceFile;
  69 
  70     private final LambdaForm lambdaForm;
  71     private final String     invokerName;
  72     private final MethodType invokerType;
  73     private final int[] localsMap;
  74 
  75     /** ASM bytecode generation. */
  76     private ClassWriter cw;
  77     private MethodVisitor mv;
  78 
  79     private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
  80     private static final Class<?> HOST_CLASS = LambdaForm.class;
  81 
  82     private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
  83                                      String className, String invokerName, MethodType invokerType) {
  84         if (invokerName.contains(".")) {
  85             int p = invokerName.indexOf(".");
  86             className = invokerName.substring(0, p);
  87             invokerName = invokerName.substring(p+1);
  88         }
  89         if (DUMP_CLASS_FILES) {
  90             className = makeDumpableClassName(className);
  91         }
  92         this.className  = superName + "$" + className;
  93         this.sourceFile = "LambdaForm$" + className;
  94         this.lambdaForm = lambdaForm;
  95         this.invokerName = invokerName;
  96         this.invokerType = invokerType;
  97         this.localsMap = new int[localsMapSize];
  98     }
  99 
 100     private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) {
 101         this(null, invokerType.parameterCount(),
 102              className, invokerName, invokerType);
 103         // Create an array to map name indexes to locals indexes.
 104         for (int i = 0; i < localsMap.length; i++) {
 105             localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i);
 106         }
 107     }
 108 
 109     private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
 110         this(form, form.names.length,
 111              className, form.debugName, invokerType);
 112         // Create an array to map name indexes to locals indexes.
 113         Name[] names = form.names;
 114         for (int i = 0, index = 0; i < localsMap.length; i++) {
 115             localsMap[i] = index;
 116             index += Wrapper.forBasicType(names[i].type).stackSlots();
 117         }
 118     }
 119 
 120 
 121     /** instance counters for dumped classes */
 122     private final static HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS;
 123     /** debugging flag for saving generated class files */
 124     private final static File DUMP_CLASS_FILES_DIR;
 125 
 126     static {
 127         if (DUMP_CLASS_FILES) {
 128             DUMP_CLASS_FILES_COUNTERS = new HashMap<>();
 129             try {
 130                 File dumpDir = new File("DUMP_CLASS_FILES");
 131                 if (!dumpDir.exists()) {
 132                     dumpDir.mkdirs();
 133                 }
 134                 DUMP_CLASS_FILES_DIR = dumpDir;
 135                 System.out.println("Dumping class files to "+DUMP_CLASS_FILES_DIR+"/...");
 136             } catch (Exception e) {
 137                 throw newInternalError(e);
 138             }
 139         } else {
 140             DUMP_CLASS_FILES_COUNTERS = null;
 141             DUMP_CLASS_FILES_DIR = null;
 142         }
 143     }
 144 
 145     static void maybeDump(final String className, final byte[] classFile) {
 146         if (DUMP_CLASS_FILES) {
 147             System.out.println("dump: " + className);
 148             java.security.AccessController.doPrivileged(
 149             new java.security.PrivilegedAction<Void>() {
 150                 public Void run() {
 151                     try {
 152                         String dumpName = className;
 153                         //dumpName = dumpName.replace('/', '-');
 154                         File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class");
 155                         dumpFile.getParentFile().mkdirs();
 156                         FileOutputStream file = new FileOutputStream(dumpFile);
 157                         file.write(classFile);
 158                         file.close();
 159                         return null;
 160                     } catch (IOException ex) {
 161                         throw newInternalError(ex);
 162                     }
 163                 }
 164             });
 165         }
 166 
 167     }
 168 
 169     private static String makeDumpableClassName(String className) {
 170         Integer ctr;
 171         synchronized (DUMP_CLASS_FILES_COUNTERS) {
 172             ctr = DUMP_CLASS_FILES_COUNTERS.get(className);
 173             if (ctr == null)  ctr = 0;
 174             DUMP_CLASS_FILES_COUNTERS.put(className, ctr+1);
 175         }
 176         String sfx = ctr.toString();
 177         while (sfx.length() < 3)
 178             sfx = "0"+sfx;
 179         className += sfx;
 180         return className;
 181     }
 182 
 183     class CpPatch {
 184         final int index;
 185         final String placeholder;
 186         final Object value;
 187         CpPatch(int index, String placeholder, Object value) {
 188             this.index = index;
 189             this.placeholder = placeholder;
 190             this.value = value;
 191         }
 192         public String toString() {
 193             return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value;
 194         }
 195     }
 196 
 197     Map<Object, CpPatch> cpPatches = new HashMap<>();
 198 
 199     int cph = 0;  // for counting constant placeholders
 200 
 201     String constantPlaceholder(Object arg) {
 202         String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
 203         if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + arg.toString() + ">>";  // debugging aid
 204         if (cpPatches.containsKey(cpPlaceholder)) {
 205             throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
 206         }
 207         // insert placeholder in CP and remember the patch
 208         int index = cw.newConst((Object) cpPlaceholder);  // TODO check if aready in the constant pool
 209         cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, arg));
 210         return cpPlaceholder;
 211     }
 212 
 213     Object[] cpPatches(byte[] classFile) {
 214         int size = getConstantPoolSize(classFile);
 215         Object[] res = new Object[size];
 216         for (CpPatch p : cpPatches.values()) {
 217             if (p.index >= size)
 218                 throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20)));
 219             res[p.index] = p.value;
 220         }
 221         return res;
 222     }
 223 
 224     /**
 225      * Extract the number of constant pool entries from a given class file.
 226      *
 227      * @param classFile the bytes of the class file in question.
 228      * @return the number of entries in the constant pool.
 229      */
 230     private static int getConstantPoolSize(byte[] classFile) {
 231         // The first few bytes:
 232         // u4 magic;
 233         // u2 minor_version;
 234         // u2 major_version;
 235         // u2 constant_pool_count;
 236         return ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF);
 237     }
 238 
 239     /**
 240      * Extract the MemberName of a newly-defined method.
 241      */
 242     private MemberName loadMethod(byte[] classFile) {
 243         Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile));
 244         return resolveInvokerMember(invokerClass, invokerName, invokerType);
 245     }
 246 
 247     /**
 248      * Define a given class as anonymous class in the runtime system.
 249      */
 250     private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) {
 251         Class<?> invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches);
 252         UNSAFE.ensureClassInitialized(invokerClass);  // Make sure the class is initialized; VM might complain.
 253         return invokerClass;
 254     }
 255 
 256     private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
 257         MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
 258         //System.out.println("resolveInvokerMember => "+member);
 259         //for (Method m : invokerClass.getDeclaredMethods())  System.out.println("  "+m);
 260         try {
 261             member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
 262         } catch (ReflectiveOperationException e) {
 263             throw newInternalError(e);
 264         }
 265         //System.out.println("resolveInvokerMember => "+member);
 266         return member;
 267     }
 268 
 269     /**
 270      * Set up class file generation.
 271      */
 272     private void classFilePrologue() {
 273         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
 274         cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null);
 275         cw.visitSource(sourceFile, null);
 276 
 277         String invokerDesc = invokerType.toMethodDescriptorString();
 278         mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null);
 279     }
 280 
 281     /**
 282      * Tear down class file generation.
 283      */
 284     private void classFileEpilogue() {
 285         mv.visitMaxs(0, 0);
 286         mv.visitEnd();
 287     }
 288 
 289     /*
 290      * Low-level emit helpers.
 291      */
 292     private void emitConst(Object con) {
 293         if (con == null) {
 294             mv.visitInsn(Opcodes.ACONST_NULL);
 295             return;
 296         }
 297         if (con instanceof Integer) {
 298             emitIconstInsn((int) con);
 299             return;
 300         }
 301         if (con instanceof Long) {
 302             long x = (long) con;
 303             if (x == (short) x) {
 304                 emitIconstInsn((int) x);
 305                 mv.visitInsn(Opcodes.I2L);
 306                 return;
 307             }
 308         }
 309         if (con instanceof Float) {
 310             float x = (float) con;
 311             if (x == (short) x) {
 312                 emitIconstInsn((int) x);
 313                 mv.visitInsn(Opcodes.I2F);
 314                 return;
 315             }
 316         }
 317         if (con instanceof Double) {
 318             double x = (double) con;
 319             if (x == (short) x) {
 320                 emitIconstInsn((int) x);
 321                 mv.visitInsn(Opcodes.I2D);
 322                 return;
 323             }
 324         }
 325         if (con instanceof Boolean) {
 326             emitIconstInsn((boolean) con ? 1 : 0);
 327             return;
 328         }
 329         // fall through:
 330         mv.visitLdcInsn(con);
 331     }
 332 
 333     private void emitIconstInsn(int i) {
 334         int opcode;
 335         switch (i) {
 336         case 0:  opcode = Opcodes.ICONST_0;  break;
 337         case 1:  opcode = Opcodes.ICONST_1;  break;
 338         case 2:  opcode = Opcodes.ICONST_2;  break;
 339         case 3:  opcode = Opcodes.ICONST_3;  break;
 340         case 4:  opcode = Opcodes.ICONST_4;  break;
 341         case 5:  opcode = Opcodes.ICONST_5;  break;
 342         default:
 343             if (i == (byte) i) {
 344                 mv.visitIntInsn(Opcodes.BIPUSH, i & 0xFF);
 345             } else if (i == (short) i) {
 346                 mv.visitIntInsn(Opcodes.SIPUSH, (char) i);
 347             } else {
 348                 mv.visitLdcInsn(i);
 349             }
 350             return;
 351         }
 352         mv.visitInsn(opcode);
 353     }
 354 
 355     /*
 356      * NOTE: These load/store methods use the localsMap to find the correct index!
 357      */
 358     private void emitLoadInsn(char type, int index) {
 359         int opcode;
 360         switch (type) {
 361         case 'I':  opcode = Opcodes.ILOAD;  break;
 362         case 'J':  opcode = Opcodes.LLOAD;  break;
 363         case 'F':  opcode = Opcodes.FLOAD;  break;
 364         case 'D':  opcode = Opcodes.DLOAD;  break;
 365         case 'L':  opcode = Opcodes.ALOAD;  break;
 366         default:
 367             throw new InternalError("unknown type: " + type);
 368         }
 369         mv.visitVarInsn(opcode, localsMap[index]);
 370     }
 371     private void emitAloadInsn(int index) {
 372         emitLoadInsn('L', index);
 373     }
 374 
 375     private void emitStoreInsn(char type, int index) {
 376         int opcode;
 377         switch (type) {
 378         case 'I':  opcode = Opcodes.ISTORE;  break;
 379         case 'J':  opcode = Opcodes.LSTORE;  break;
 380         case 'F':  opcode = Opcodes.FSTORE;  break;
 381         case 'D':  opcode = Opcodes.DSTORE;  break;
 382         case 'L':  opcode = Opcodes.ASTORE;  break;
 383         default:
 384             throw new InternalError("unknown type: " + type);
 385         }
 386         mv.visitVarInsn(opcode, localsMap[index]);
 387     }
 388     private void emitAstoreInsn(int index) {
 389         emitStoreInsn('L', index);
 390     }
 391 
 392     /**
 393      * Emit a boxing call.
 394      *
 395      * @param type primitive type class to box.
 396      */
 397     private void emitBoxing(Class<?> type) {
 398         Wrapper wrapper = Wrapper.forPrimitiveType(type);
 399         String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
 400         String name  = "valueOf";
 401         String desc  = "(" + wrapper.basicTypeChar() + ")L" + owner + ";";
 402         mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc);
 403     }
 404 
 405     /**
 406      * Emit an unboxing call (plus preceding checkcast).
 407      *
 408      * @param type wrapper type class to unbox.
 409      */
 410     private void emitUnboxing(Class<?> type) {
 411         Wrapper wrapper = Wrapper.forWrapperType(type);
 412         String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
 413         String name  = wrapper.primitiveSimpleName() + "Value";
 414         String desc  = "()" + wrapper.basicTypeChar();
 415         mv.visitTypeInsn(Opcodes.CHECKCAST, owner);
 416         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc);
 417     }
 418 
 419     /**
 420      * Emit an implicit conversion.
 421      *
 422      * @param ptype type of value present on stack
 423      * @param pclass type of value required on stack
 424      */
 425     private void emitImplicitConversion(char ptype, Class<?> pclass) {
 426         switch (ptype) {
 427         case 'L':
 428             if (VerifyType.isNullConversion(Object.class, pclass))
 429                 return;
 430             if (isStaticallyNameable(pclass)) {
 431                 mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass));
 432             } else {
 433                 mv.visitLdcInsn(constantPlaceholder(pclass));
 434                 mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
 435                 mv.visitInsn(Opcodes.SWAP);
 436                 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG);
 437                 if (pclass.isArray())
 438                     mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
 439             }
 440             return;
 441         case 'I':
 442             if (!VerifyType.isNullConversion(int.class, pclass))
 443                 emitPrimCast(ptype, Wrapper.basicTypeChar(pclass));
 444             return;
 445         case 'J':
 446             assert(pclass == long.class);
 447             return;
 448         case 'F':
 449             assert(pclass == float.class);
 450             return;
 451         case 'D':
 452             assert(pclass == double.class);
 453             return;
 454         }
 455         throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass);
 456     }
 457 
 458     /**
 459      * Emits an actual return instruction conforming to the given return type.
 460      */
 461     private void emitReturnInsn(Class<?> type) {
 462         int opcode;
 463         switch (Wrapper.basicTypeChar(type)) {
 464         case 'I':  opcode = Opcodes.IRETURN;  break;
 465         case 'J':  opcode = Opcodes.LRETURN;  break;
 466         case 'F':  opcode = Opcodes.FRETURN;  break;
 467         case 'D':  opcode = Opcodes.DRETURN;  break;
 468         case 'L':  opcode = Opcodes.ARETURN;  break;
 469         case 'V':  opcode = Opcodes.RETURN;   break;
 470         default:
 471             throw new InternalError("unknown return type: " + type);
 472         }
 473         mv.visitInsn(opcode);
 474     }
 475 
 476     private static String getInternalName(Class<?> c) {
 477         assert(VerifyAccess.isTypeVisible(c, Object.class));
 478         return c.getName().replace('.', '/');
 479     }
 480 
 481     /**
 482      * Generate customized bytecode for a given LambdaForm.
 483      */
 484     static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
 485         InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType);
 486         return g.loadMethod(g.generateCustomizedCodeBytes());
 487     }
 488 
 489     /**
 490      * Generate an invoker method for the passed {@link LambdaForm}.
 491      */
 492     private byte[] generateCustomizedCodeBytes() {
 493         classFilePrologue();
 494 
 495         // Suppress this method in backtraces displayed to the user.
 496         mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
 497 
 498         // Mark this method as a compiled LambdaForm
 499         mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
 500 
 501         // Force inlining of this invoker method.
 502         mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
 503 
 504         // iterate over the form's names, generating bytecode instructions for each
 505         // start iterating at the first name following the arguments
 506         for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
 507             Name name = lambdaForm.names[i];
 508             MemberName member = name.function.member();
 509 
 510             if (isSelectAlternative(member)) {
 511                 // selectAlternative idiom
 512                 // FIXME: make sure this idiom is really present!
 513                 emitSelectAlternative(name, lambdaForm.names[i + 1]);
 514                 i++;  // skip MH.invokeBasic of the selectAlternative result
 515             } else if (isGuardWithCatch(i)) {
 516                 emitGuardWithCatch(i);
 517                 i = i+2; // Jump to the end of GWC idiom
 518             } else if (isStaticallyInvocable(member)) {
 519                 emitStaticInvoke(member, name);
 520             } else {
 521                 emitInvoke(name);
 522             }
 523 
 524             // Update cached form name's info in case an intrinsic spanning multiple names was encountered.
 525             name = lambdaForm.names[i];
 526             member = name.function.member();
 527 
 528             // store the result from evaluating to the target name in a local if required
 529             // (if this is the last value, i.e., the one that is going to be returned,
 530             // avoid store/load/return and just return)
 531             if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) {
 532                 // return value - do nothing
 533             } else if (name.type != 'V') {
 534                 // non-void: actually assign
 535                 emitStoreInsn(name.type, name.index());
 536             }
 537         }
 538 
 539         // return statement
 540         emitReturn();
 541 
 542         classFileEpilogue();
 543         bogusMethod(lambdaForm);
 544 
 545         final byte[] classFile = cw.toByteArray();
 546         maybeDump(className, classFile);
 547         return classFile;
 548     }
 549 
 550     /**
 551      * Emit an invoke for the given name.
 552      */
 553     void emitInvoke(Name name) {
 554         if (true) {
 555             // push receiver
 556             MethodHandle target = name.function.resolvedHandle;
 557             assert(target != null) : name.exprString();
 558             mv.visitLdcInsn(constantPlaceholder(target));
 559             mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
 560         } else {
 561             // load receiver
 562             emitAloadInsn(0);
 563             mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
 564             mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
 565             mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
 566             // TODO more to come
 567         }
 568 
 569         // push arguments
 570         for (int i = 0; i < name.arguments.length; i++) {
 571             emitPushArgument(name, i);
 572         }
 573 
 574         // invocation
 575         MethodType type = name.function.methodType();
 576         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString());
 577     }
 578 
 579     static private Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
 580         // Sample classes from each package we are willing to bind to statically:
 581         java.lang.Object.class,
 582         java.util.Arrays.class,
 583         sun.misc.Unsafe.class
 584         //MethodHandle.class already covered
 585     };
 586 
 587     static boolean isStaticallyInvocable(MemberName member) {
 588         if (member == null)  return false;
 589         if (member.isConstructor())  return false;
 590         Class<?> cls = member.getDeclaringClass();
 591         if (cls.isArray() || cls.isPrimitive())
 592             return false;  // FIXME
 593         if (cls.isAnonymousClass() || cls.isLocalClass())
 594             return false;  // inner class of some sort
 595         if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
 596             return false;  // not on BCP
 597         MethodType mtype = member.getMethodOrFieldType();
 598         if (!isStaticallyNameable(mtype.returnType()))
 599             return false;
 600         for (Class<?> ptype : mtype.parameterArray())
 601             if (!isStaticallyNameable(ptype))
 602                 return false;
 603         if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls))
 604             return true;   // in java.lang.invoke package
 605         if (member.isPublic() && isStaticallyNameable(cls))
 606             return true;
 607         return false;
 608     }
 609 
 610     static boolean isStaticallyNameable(Class<?> cls) {
 611         while (cls.isArray())
 612             cls = cls.getComponentType();
 613         if (cls.isPrimitive())
 614             return true;  // int[].class, for example
 615         // could use VerifyAccess.isClassAccessible but the following is a safe approximation
 616         if (cls.getClassLoader() != Object.class.getClassLoader())
 617             return false;
 618         if (VerifyAccess.isSamePackage(MethodHandle.class, cls))
 619             return true;
 620         if (!Modifier.isPublic(cls.getModifiers()))
 621             return false;
 622         for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) {
 623             if (VerifyAccess.isSamePackage(pkgcls, cls))
 624                 return true;
 625         }
 626         return false;
 627     }
 628 
 629     /**
 630      * Emit an invoke for the given name, using the MemberName directly.
 631      */
 632     void emitStaticInvoke(MemberName member, Name name) {
 633         assert(member.equals(name.function.member()));
 634         String cname = getInternalName(member.getDeclaringClass());
 635         String mname = member.getName();
 636         String mtype;
 637         byte refKind = member.getReferenceKind();
 638         if (refKind == REF_invokeSpecial) {
 639             // in order to pass the verifier, we need to convert this to invokevirtual in all cases
 640             assert(member.canBeStaticallyBound()) : member;
 641             refKind = REF_invokeVirtual;
 642         }
 643 
 644         if (member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual) {
 645             // Methods from Object declared in an interface can be resolved by JVM to invokevirtual kind.
 646             // Need to convert it back to invokeinterface to pass verification and make the invocation works as expected.
 647             refKind = REF_invokeInterface;
 648         }
 649 
 650         // push arguments
 651         for (int i = 0; i < name.arguments.length; i++) {
 652             emitPushArgument(name, i);
 653         }
 654 
 655         // invocation
 656         if (member.isMethod()) {
 657             mtype = member.getMethodType().toMethodDescriptorString();
 658             mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype,
 659                                member.getDeclaringClass().isInterface());
 660         } else {
 661             mtype = MethodType.toFieldDescriptorString(member.getFieldType());
 662             mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
 663         }
 664     }
 665     int refKindOpcode(byte refKind) {
 666         switch (refKind) {
 667         case REF_invokeVirtual:      return Opcodes.INVOKEVIRTUAL;
 668         case REF_invokeStatic:       return Opcodes.INVOKESTATIC;
 669         case REF_invokeSpecial:      return Opcodes.INVOKESPECIAL;
 670         case REF_invokeInterface:    return Opcodes.INVOKEINTERFACE;
 671         case REF_getField:           return Opcodes.GETFIELD;
 672         case REF_putField:           return Opcodes.PUTFIELD;
 673         case REF_getStatic:          return Opcodes.GETSTATIC;
 674         case REF_putStatic:          return Opcodes.PUTSTATIC;
 675         }
 676         throw new InternalError("refKind="+refKind);
 677     }
 678 
 679     /**
 680      *  Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}.
 681      */
 682     private boolean memberNameRefersTo(MemberName member, Class<?> declaringClass, String name) {
 683         return member != null &&
 684                member.getDeclaringClass() == declaringClass &&
 685                member.getName().equals(name);
 686     }
 687 
 688     /**
 689      * Check if MemberName is a call to MethodHandleImpl.selectAlternative.
 690      */
 691     private boolean isSelectAlternative(MemberName member) {
 692         return memberNameRefersTo(member, MethodHandleImpl.class, "selectAlternative");
 693     }
 694 
 695     /**
 696      * Emit bytecode for the selectAlternative idiom.
 697      *
 698      * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
 699      * <blockquote><pre>{@code
 700      *   Lambda(a0:L,a1:I)=>{
 701      *     t2:I=foo.test(a1:I);
 702      *     t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
 703      *     t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
 704      * }</pre></blockquote>
 705      */
 706     private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
 707         MethodType type = selectAlternativeName.function.methodType();
 708 
 709         Name receiver = (Name) invokeBasicName.arguments[0];
 710 
 711         Label L_fallback = new Label();
 712         Label L_done     = new Label();
 713 
 714         // load test result
 715         emitPushArgument(selectAlternativeName, 0);
 716         mv.visitInsn(Opcodes.ICONST_1);
 717 
 718         // if_icmpne L_fallback
 719         mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
 720 
 721         // invoke selectAlternativeName.arguments[1]
 722         MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1];
 723         emitPushArgument(selectAlternativeName, 1);  // get 2nd argument of selectAlternative
 724         emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
 725         emitInvoke(invokeBasicName);
 726 
 727         // goto L_done
 728         mv.visitJumpInsn(Opcodes.GOTO, L_done);
 729 
 730         // L_fallback:
 731         mv.visitLabel(L_fallback);
 732 
 733         // invoke selectAlternativeName.arguments[2]
 734         MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2];
 735         emitPushArgument(selectAlternativeName, 2);  // get 3rd argument of selectAlternative
 736         emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
 737         emitInvoke(invokeBasicName);
 738 
 739         // L_done:
 740         mv.visitLabel(L_done);
 741     }
 742 
 743     /**
 744      * Check if i-th name is a start of GuardWithCatch idiom.
 745      */
 746     private boolean isGuardWithCatch(int pos) {
 747         // GuardWithCatch idiom:
 748         //     t_{n}:L=MethodHandle.invokeBasic(...)
 749         //     t_{n+1}:L=MethodHandleImpl.guardWithCatch(...);
 750         //     t_{n+2}:I=MethodHandle.invokeBasic(...)
 751         return lambdaForm.names.length-1 >= pos+2 &&
 752                memberNameRefersTo(lambdaForm.names[pos+1].function.member(),
 753                                   MethodHandleImpl.class, "guardWithCatch");
 754     }
 755 
 756     /**
 757       * Emit bytecode for the guardWithCatch idiom.
 758       *
 759       * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch):
 760       * <blockquote><pre>{@code
 761       *  guardWithCatch=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{
 762       *    t8:L=MethodHandle.invokeBasic(a4:L,a6:L,a7:L);
 763       *    t9:L=MethodHandleImpl.guardWithCatch(a1:L,a2:L,a3:L,t8:L);
 764       *   t10:I=MethodHandle.invokeBasic(a5:L,t9:L);t10:I}
 765       * }</pre></blockquote>
 766       *
 767       * It is compiled into bytecode equivalent of the following code:
 768       * <blockquote><pre>{@code
 769       *  try {
 770       *      return a1.invokeBasic(a6, a7);
 771       *  } catch (Throwable e) {
 772       *      if (!a2.isInstance(e)) throw e;
 773       *      return a3.invokeBasic(ex, a6, a7);
 774       *  }}
 775       */
 776     private void emitGuardWithCatch(int pos) {
 777         Name args    = lambdaForm.names[pos];
 778         Name invoker = lambdaForm.names[pos+1];
 779         Name result  = lambdaForm.names[pos+2];
 780 
 781         Label L_startBlock = new Label();
 782         Label L_endBlock = new Label();
 783         Label L_handler = new Label();
 784         Label L_done = new Label();
 785 
 786         Class<?> returnType = result.function.resolvedHandle.type().returnType();
 787         MethodType type = args.function.resolvedHandle.type()
 788                               .dropParameterTypes(0,1)
 789                               .changeReturnType(returnType);
 790 
 791         mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_handler, "java/lang/Throwable");
 792 
 793         // Normal case
 794         mv.visitLabel(L_startBlock);
 795         // load target
 796         emitPushArgument(invoker, 0);
 797         emitPushArguments(args, 1); // skip 1st argument: method handle
 798         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString());
 799         mv.visitLabel(L_endBlock);
 800         mv.visitJumpInsn(Opcodes.GOTO, L_done);
 801 
 802         // Exceptional case
 803         mv.visitLabel(L_handler);
 804 
 805         // Check exception's type
 806         mv.visitInsn(Opcodes.DUP);
 807         // load exception class
 808         emitPushArgument(invoker, 1);
 809         mv.visitInsn(Opcodes.SWAP);
 810         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z");
 811         Label L_rethrow = new Label();
 812         mv.visitJumpInsn(Opcodes.IFEQ, L_rethrow);
 813 
 814         // Invoke catcher
 815         // load catcher
 816         emitPushArgument(invoker, 2);
 817         mv.visitInsn(Opcodes.SWAP);
 818         emitPushArguments(args, 1); // skip 1st argument: method handle
 819         MethodType catcherType = type.insertParameterTypes(0, Throwable.class);
 820         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", catcherType.basicType().toMethodDescriptorString());
 821         mv.visitJumpInsn(Opcodes.GOTO, L_done);
 822 
 823         mv.visitLabel(L_rethrow);
 824         mv.visitInsn(Opcodes.ATHROW);
 825 
 826         mv.visitLabel(L_done);
 827     }
 828 
 829     private void emitPushArguments(Name args, int start) {
 830         for (int i = start; i < args.arguments.length; i++) {
 831             emitPushArgument(args, i);
 832         }
 833     }
 834 
 835     private void emitPushArgument(Name name, int paramIndex) {
 836         Object arg = name.arguments[paramIndex];
 837         char ptype = name.function.parameterType(paramIndex);
 838         MethodType mtype = name.function.methodType();
 839         if (arg instanceof Name) {
 840             Name n = (Name) arg;
 841             emitLoadInsn(n.type, n.index());
 842             emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
 843         } else if ((arg == null || arg instanceof String) && ptype == 'L') {
 844             emitConst(arg);
 845         } else {
 846             if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') {
 847                 emitConst(arg);
 848             } else {
 849                 mv.visitLdcInsn(constantPlaceholder(arg));
 850                 emitImplicitConversion('L', mtype.parameterType(paramIndex));
 851             }
 852         }
 853     }
 854 
 855     /**
 856      * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
 857      */
 858     private void emitReturn() {
 859         // return statement
 860         if (lambdaForm.result == -1) {
 861             // void
 862             mv.visitInsn(Opcodes.RETURN);
 863         } else {
 864             LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
 865             char rtype = Wrapper.basicTypeChar(invokerType.returnType());
 866 
 867             // put return value on the stack if it is not already there
 868             if (lambdaForm.result != lambdaForm.names.length - 1) {
 869                 emitLoadInsn(rn.type, lambdaForm.result);
 870             }
 871 
 872             // potentially generate cast
 873             // rtype is the return type of the invoker - generated code must conform to this
 874             // rn.type is the type of the result Name in the LF
 875             if (rtype != rn.type) {
 876                 // need cast
 877                 if (rtype == 'L') {
 878                     // possibly cast the primitive to the correct type for boxing
 879                     char boxedType = Wrapper.forWrapperType(invokerType.returnType()).basicTypeChar();
 880                     if (boxedType != rn.type) {
 881                         emitPrimCast(rn.type, boxedType);
 882                     }
 883                     // cast primitive to reference ("boxing")
 884                     emitBoxing(invokerType.returnType());
 885                 } else {
 886                     // to-primitive cast
 887                     if (rn.type != 'L') {
 888                         // prim-to-prim cast
 889                         emitPrimCast(rn.type, rtype);
 890                     } else {
 891                         // ref-to-prim cast ("unboxing")
 892                         throw new InternalError("no ref-to-prim (unboxing) casts supported right now");
 893                     }
 894                 }
 895             }
 896 
 897             // generate actual return statement
 898             emitReturnInsn(invokerType.returnType());
 899         }
 900     }
 901 
 902     /**
 903      * Emit a type conversion bytecode casting from "from" to "to".
 904      */
 905     private void emitPrimCast(char from, char to) {
 906         // Here's how.
 907         // -   indicates forbidden
 908         // <-> indicates implicit
 909         //      to ----> boolean  byte     short    char     int      long     float    double
 910         // from boolean    <->        -        -        -        -        -        -        -
 911         //      byte        -       <->       i2s      i2c      <->      i2l      i2f      i2d
 912         //      short       -       i2b       <->      i2c      <->      i2l      i2f      i2d
 913         //      char        -       i2b       i2s      <->      <->      i2l      i2f      i2d
 914         //      int         -       i2b       i2s      i2c      <->      i2l      i2f      i2d
 915         //      long        -     l2i,i2b   l2i,i2s  l2i,i2c    l2i      <->      l2f      l2d
 916         //      float       -     f2i,i2b   f2i,i2s  f2i,i2c    f2i      f2l      <->      f2d
 917         //      double      -     d2i,i2b   d2i,i2s  d2i,i2c    d2i      d2l      d2f      <->
 918         if (from == to) {
 919             // no cast required, should be dead code anyway
 920             return;
 921         }
 922         Wrapper wfrom = Wrapper.forBasicType(from);
 923         Wrapper wto   = Wrapper.forBasicType(to);
 924         if (wfrom.isSubwordOrInt()) {
 925             // cast from {byte,short,char,int} to anything
 926             emitI2X(to);
 927         } else {
 928             // cast from {long,float,double} to anything
 929             if (wto.isSubwordOrInt()) {
 930                 // cast to {byte,short,char,int}
 931                 emitX2I(from);
 932                 if (wto.bitWidth() < 32) {
 933                     // targets other than int require another conversion
 934                     emitI2X(to);
 935                 }
 936             } else {
 937                 // cast to {long,float,double} - this is verbose
 938                 boolean error = false;
 939                 switch (from) {
 940                 case 'J':
 941                          if (to == 'F') { mv.visitInsn(Opcodes.L2F); }
 942                     else if (to == 'D') { mv.visitInsn(Opcodes.L2D); }
 943                     else error = true;
 944                     break;
 945                 case 'F':
 946                          if (to == 'J') { mv.visitInsn(Opcodes.F2L); }
 947                     else if (to == 'D') { mv.visitInsn(Opcodes.F2D); }
 948                     else error = true;
 949                     break;
 950                 case 'D':
 951                          if (to == 'J') { mv.visitInsn(Opcodes.D2L); }
 952                     else if (to == 'F') { mv.visitInsn(Opcodes.D2F); }
 953                     else error = true;
 954                     break;
 955                 default:
 956                     error = true;
 957                     break;
 958                 }
 959                 if (error) {
 960                     throw new IllegalStateException("unhandled prim cast: " + from + "2" + to);
 961                 }
 962             }
 963         }
 964     }
 965 
 966     private void emitI2X(char type) {
 967         switch (type) {
 968         case 'B':  mv.visitInsn(Opcodes.I2B);  break;
 969         case 'S':  mv.visitInsn(Opcodes.I2S);  break;
 970         case 'C':  mv.visitInsn(Opcodes.I2C);  break;
 971         case 'I':  /* naught */                break;
 972         case 'J':  mv.visitInsn(Opcodes.I2L);  break;
 973         case 'F':  mv.visitInsn(Opcodes.I2F);  break;
 974         case 'D':  mv.visitInsn(Opcodes.I2D);  break;
 975         case 'Z':
 976             // For compatibility with ValueConversions and explicitCastArguments:
 977             mv.visitInsn(Opcodes.ICONST_1);
 978             mv.visitInsn(Opcodes.IAND);
 979             break;
 980         default:   throw new InternalError("unknown type: " + type);
 981         }
 982     }
 983 
 984     private void emitX2I(char type) {
 985         switch (type) {
 986         case 'J':  mv.visitInsn(Opcodes.L2I);  break;
 987         case 'F':  mv.visitInsn(Opcodes.F2I);  break;
 988         case 'D':  mv.visitInsn(Opcodes.D2I);  break;
 989         default:   throw new InternalError("unknown type: " + type);
 990         }
 991     }
 992 
 993     private static String basicTypeCharSignature(String prefix, MethodType type) {
 994         StringBuilder buf = new StringBuilder(prefix);
 995         for (Class<?> ptype : type.parameterList())
 996             buf.append(Wrapper.forBasicType(ptype).basicTypeChar());
 997         buf.append('_').append(Wrapper.forBasicType(type.returnType()).basicTypeChar());
 998         return buf.toString();
 999     }
1000 
1001     /**
1002      * Generate bytecode for a LambdaForm.vmentry which calls interpretWithArguments.
1003      */
1004     static MemberName generateLambdaFormInterpreterEntryPoint(String sig) {
1005         assert(LambdaForm.isValidSignature(sig));
1006         //System.out.println("generateExactInvoker "+sig);
1007         // compute method type
1008         // first parameter and return type
1009         char tret = LambdaForm.signatureReturn(sig);
1010         MethodType type = MethodType.methodType(LambdaForm.typeClass(tret), MethodHandle.class);
1011         // other parameter types
1012         int arity = LambdaForm.signatureArity(sig);
1013         for (int i = 1; i < arity; i++) {
1014             type = type.appendParameterTypes(LambdaForm.typeClass(sig.charAt(i)));
1015         }
1016         InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", "interpret_"+tret, type);
1017         return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes());
1018     }
1019 
1020     private byte[] generateLambdaFormInterpreterEntryPointBytes() {
1021         classFilePrologue();
1022 
1023         // Suppress this method in backtraces displayed to the user.
1024         mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
1025 
1026         // Don't inline the interpreter entry.
1027         mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true);
1028 
1029         // create parameter array
1030         emitIconstInsn(invokerType.parameterCount());
1031         mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
1032 
1033         // fill parameter array
1034         for (int i = 0; i < invokerType.parameterCount(); i++) {
1035             Class<?> ptype = invokerType.parameterType(i);
1036             mv.visitInsn(Opcodes.DUP);
1037             emitIconstInsn(i);
1038             emitLoadInsn(Wrapper.basicTypeChar(ptype), i);
1039             // box if primitive type
1040             if (ptype.isPrimitive()) {
1041                 emitBoxing(ptype);
1042             }
1043             mv.visitInsn(Opcodes.AASTORE);
1044         }
1045         // invoke
1046         emitAloadInsn(0);
1047         mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;");
1048         mv.visitInsn(Opcodes.SWAP);  // swap form and array; avoid local variable
1049         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;");
1050 
1051         // maybe unbox
1052         Class<?> rtype = invokerType.returnType();
1053         if (rtype.isPrimitive() && rtype != void.class) {
1054             emitUnboxing(Wrapper.asWrapperType(rtype));
1055         }
1056 
1057         // return statement
1058         emitReturnInsn(rtype);
1059 
1060         classFileEpilogue();
1061         bogusMethod(invokerType);
1062 
1063         final byte[] classFile = cw.toByteArray();
1064         maybeDump(className, classFile);
1065         return classFile;
1066     }
1067 
1068     /**
1069      * Generate bytecode for a NamedFunction invoker.
1070      */
1071     static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) {
1072         MethodType invokerType = LambdaForm.NamedFunction.INVOKER_METHOD_TYPE;
1073         String invokerName = basicTypeCharSignature("invoke_", typeForm.erasedType());
1074         InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType);
1075         return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm));
1076     }
1077 
1078     static int nfi = 0;
1079 
1080     private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) {
1081         MethodType dstType = typeForm.erasedType();
1082         classFilePrologue();
1083 
1084         // Suppress this method in backtraces displayed to the user.
1085         mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
1086 
1087         // Force inlining of this invoker method.
1088         mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
1089 
1090         // Load receiver
1091         emitAloadInsn(0);
1092 
1093         // Load arguments from array
1094         for (int i = 0; i < dstType.parameterCount(); i++) {
1095             emitAloadInsn(1);
1096             emitIconstInsn(i);
1097             mv.visitInsn(Opcodes.AALOAD);
1098 
1099             // Maybe unbox
1100             Class<?> dptype = dstType.parameterType(i);
1101             if (dptype.isPrimitive()) {
1102                 Class<?> sptype = dstType.basicType().wrap().parameterType(i);
1103                 Wrapper dstWrapper = Wrapper.forBasicType(dptype);
1104                 Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper;  // narrow subword from int
1105                 emitUnboxing(srcWrapper.wrapperType());
1106                 emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
1107             }
1108         }
1109 
1110         // Invoke
1111         String targetDesc = dstType.basicType().toMethodDescriptorString();
1112         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", targetDesc);
1113 
1114         // Box primitive types
1115         Class<?> rtype = dstType.returnType();
1116         if (rtype != void.class && rtype.isPrimitive()) {
1117             Wrapper srcWrapper = Wrapper.forBasicType(rtype);
1118             Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper;  // widen subword to int
1119             // boolean casts not allowed
1120             emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
1121             emitBoxing(dstWrapper.primitiveType());
1122         }
1123 
1124         // If the return type is void we return a null reference.
1125         if (rtype == void.class) {
1126             mv.visitInsn(Opcodes.ACONST_NULL);
1127         }
1128         emitReturnInsn(Object.class);  // NOTE: NamedFunction invokers always return a reference value.
1129 
1130         classFileEpilogue();
1131         bogusMethod(dstType);
1132 
1133         final byte[] classFile = cw.toByteArray();
1134         maybeDump(className, classFile);
1135         return classFile;
1136     }
1137 
1138     /**
1139      * Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool
1140      * for debugging purposes.
1141      */
1142     private void bogusMethod(Object... os) {
1143         if (DUMP_CLASS_FILES) {
1144             mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null);
1145             for (Object o : os) {
1146                 mv.visitLdcInsn(o.toString());
1147                 mv.visitInsn(Opcodes.POP);
1148             }
1149             mv.visitInsn(Opcodes.RETURN);
1150             mv.visitMaxs(0, 0);
1151             mv.visitEnd();
1152         }
1153     }
1154 }