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