changeset 1565:26d93df3905a

7194586: Add back-end support for invokedynamic Summary: Add support for invokedynamic bytecode instruction; includes suppot for generation of all related classfile attributes Reviewed-by: jjg
author mcimadamore
date Tue, 25 Sep 2012 11:53:18 +0100
parents 99983a4a593b
children 2eca84194807
files src/share/classes/com/sun/tools/javac/code/Symbol.java src/share/classes/com/sun/tools/javac/code/Symtab.java src/share/classes/com/sun/tools/javac/jvm/ClassFile.java src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java src/share/classes/com/sun/tools/javac/jvm/Code.java src/share/classes/com/sun/tools/javac/jvm/Gen.java src/share/classes/com/sun/tools/javac/jvm/Items.java src/share/classes/com/sun/tools/javac/jvm/Pool.java src/share/classes/com/sun/tools/javac/util/Names.java test/tools/javac/lambda/TestInvokeDynamic.java
diffstat 10 files changed, 692 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Sep 25 11:52:37 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Sep 25 11:53:18 2012 +0100
@@ -1068,6 +1068,10 @@
             }
         }
 
+        public boolean isDynamic() {
+            return false;
+        }
+
         /** find a symbol that this (proxy method) symbol implements.
          *  @param    c       The class whose members are searched for
          *                    implementations
@@ -1356,6 +1360,27 @@
         }
     }
 
+    /** A class for invokedynamic method calls.
+     */
+    public static class DynamicMethodSymbol extends MethodSymbol {
+
+        public Object[] staticArgs;
+        public Symbol bsm;
+        public int bsmKind;
+
+        public DynamicMethodSymbol(Name name, Symbol owner, int bsmKind, MethodSymbol bsm, Type type, Object[] staticArgs) {
+            super(0, name, type, owner);
+            this.bsm = bsm;
+            this.bsmKind = bsmKind;
+            this.staticArgs = staticArgs;
+        }
+
+        @Override
+        public boolean isDynamic() {
+            return true;
+        }
+    }
+
     /** A class for predefined operators.
      */
     public static class OperatorSymbol extends MethodSymbol {
--- a/src/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Sep 25 11:52:37 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Sep 25 11:53:18 2012 +0100
@@ -126,6 +126,7 @@
     public final Type cloneableType;
     public final Type serializableType;
     public final Type methodHandleType;
+    public final Type methodTypeType;
     public final Type nativeHeaderType;
     public final Type throwableType;
     public final Type errorType;
@@ -440,6 +441,7 @@
         throwableType = enterClass("java.lang.Throwable");
         serializableType = enterClass("java.io.Serializable");
         methodHandleType = enterClass("java.lang.invoke.MethodHandle");
+        methodTypeType = enterClass("java.lang.invoke.MethodType");
         errorType = enterClass("java.lang.Error");
         illegalArgumentExceptionType = enterClass("java.lang.IllegalArgumentException");
         interruptedExceptionType = enterClass("java.lang.InterruptedException");
--- a/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java	Tue Sep 25 11:52:37 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java	Tue Sep 25 11:53:18 2012 +0100
@@ -84,6 +84,16 @@
     public final static int CONSTANT_MethodType = 16;
     public final static int CONSTANT_InvokeDynamic = 18;
 
+    public final static int REF_getField = 1;
+    public final static int REF_getStatic = 2;
+    public final static int REF_putField = 3;
+    public final static int REF_putStatic = 4;
+    public final static int REF_invokeVirtual = 5;
+    public final static int REF_invokeStatic = 6;
+    public final static int REF_invokeSpecial = 7;
+    public final static int REF_newInvokeSpecial = 8;
+    public final static int REF_invokeInterface = 9;
+
     public final static int MAX_PARAMETERS = 0xff;
     public final static int MAX_DIMENSIONS = 0xff;
     public final static int MAX_CODE = 0xffff;
--- a/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Tue Sep 25 11:52:37 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Tue Sep 25 11:53:18 2012 +0100
@@ -26,6 +26,8 @@
 package com.sun.tools.javac.jvm;
 
 import java.io.*;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.Set;
 import java.util.HashSet;
 
@@ -137,6 +139,11 @@
      */
     ListBuffer<ClassSymbol> innerClassesQueue;
 
+    /** The bootstrap methods to be written in the corresponding class attribute
+     *  (one for each invokedynamic)
+     */
+    Map<MethodSymbol, Pool.MethodHandle> bootstrapMethods;
+
     /** The log to use for verbose output.
      */
     private final Log log;
@@ -477,11 +484,27 @@
 
             if (value instanceof MethodSymbol) {
                 MethodSymbol m = (MethodSymbol)value;
-                poolbuf.appendByte((m.owner.flags() & INTERFACE) != 0
-                          ? CONSTANT_InterfaceMethodref
-                          : CONSTANT_Methodref);
-                poolbuf.appendChar(pool.put(m.owner));
-                poolbuf.appendChar(pool.put(nameType(m)));
+                if (!m.isDynamic()) {
+                    poolbuf.appendByte((m.owner.flags() & INTERFACE) != 0
+                              ? CONSTANT_InterfaceMethodref
+                              : CONSTANT_Methodref);
+                    poolbuf.appendChar(pool.put(m.owner));
+                    poolbuf.appendChar(pool.put(nameType(m)));
+                } else {
+                    //invokedynamic
+                    DynamicMethodSymbol dynSym = (DynamicMethodSymbol)m;
+                    Pool.MethodHandle handle = new Pool.MethodHandle(dynSym.bsmKind, dynSym.bsm, names);
+                    bootstrapMethods.put(dynSym, handle);
+                    //init cp entries
+                    pool.put(names.BootstrapMethods);
+                    pool.put(handle);
+                    for (Object staticArg : dynSym.staticArgs) {
+                        pool.put(staticArg);
+                    }
+                    poolbuf.appendByte(CONSTANT_InvokeDynamic);
+                    poolbuf.appendChar(bootstrapMethods.size() - 1);
+                    poolbuf.appendChar(pool.put(nameType(dynSym)));
+                }
             } else if (value instanceof VarSymbol) {
                 VarSymbol v = (VarSymbol)value;
                 poolbuf.appendByte(CONSTANT_Fieldref);
@@ -526,11 +549,20 @@
             } else if (value instanceof String) {
                 poolbuf.appendByte(CONSTANT_String);
                 poolbuf.appendChar(pool.put(names.fromString((String)value)));
+            } else if (value instanceof MethodType) {
+                MethodType mtype = (MethodType)value;
+                poolbuf.appendByte(CONSTANT_MethodType);
+                poolbuf.appendChar(pool.put(typeSig(mtype)));
             } else if (value instanceof Type) {
                 Type type = (Type)value;
                 if (type.tag == CLASS) enterInner((ClassSymbol)type.tsym);
                 poolbuf.appendByte(CONSTANT_Class);
                 poolbuf.appendChar(pool.put(xClassName(type)));
+            } else if (value instanceof Pool.MethodHandle) {
+                Pool.MethodHandle ref = (Pool.MethodHandle)value;
+                poolbuf.appendByte(CONSTANT_MethodHandle);
+                poolbuf.appendByte(ref.refKind);
+                poolbuf.appendChar(pool.put(ref.refSym));
             } else {
                 Assert.error("writePool " + value);
             }
@@ -914,6 +946,25 @@
         endAttr(alenIdx);
     }
 
+    /** Write "bootstrapMethods" attribute.
+     */
+    void writeBootstrapMethods() {
+        int alenIdx = writeAttr(names.BootstrapMethods);
+        databuf.appendChar(bootstrapMethods.size());
+        for (Map.Entry<MethodSymbol, Pool.MethodHandle> entry : bootstrapMethods.entrySet()) {
+            DynamicMethodSymbol dsym = (DynamicMethodSymbol)entry.getKey();
+            //write BSM handle
+            databuf.appendChar(pool.get(entry.getValue()));
+            //write static args length
+            databuf.appendChar(dsym.staticArgs.length);
+            //write static args array
+            for (Object o : dsym.staticArgs) {
+                databuf.appendChar(pool.get(o));
+            }
+        }
+        endAttr(alenIdx);
+    }
+
     /** Write field symbol, entering all references into constant pool.
      */
     void writeField(VarSymbol v) {
@@ -1483,6 +1534,7 @@
         pool = c.pool;
         innerClasses = null;
         innerClassesQueue = null;
+        bootstrapMethods = new LinkedHashMap<MethodSymbol, Pool.MethodHandle>();
 
         Type supertype = types.supertype(c.type);
         List<Type> interfaces = types.interfaces(c.type);
@@ -1589,6 +1641,12 @@
             writeInnerClasses();
             acount++;
         }
+
+        if (!bootstrapMethods.isEmpty()) {
+            writeBootstrapMethods();
+            acount++;
+        }
+
         endAttrs(acountIdx, acount);
 
         poolbuf.appendBytes(databuf.elems, 0, databuf.length);
--- a/src/share/classes/com/sun/tools/javac/jvm/Code.java	Tue Sep 25 11:52:37 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/Code.java	Tue Sep 25 11:53:18 2012 +0100
@@ -903,6 +903,8 @@
         if (o instanceof Double) return syms.doubleType;
         if (o instanceof ClassSymbol) return syms.classType;
         if (o instanceof Type.ArrayType) return syms.classType;
+        if (o instanceof Type.MethodType) return syms.methodTypeType;
+        if (o instanceof Pool.MethodHandle) return syms.methodHandleType;
         throw new AssertionError(o);
     }
 
--- a/src/share/classes/com/sun/tools/javac/jvm/Gen.java	Tue Sep 25 11:52:37 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/Gen.java	Tue Sep 25 11:53:18 2012 +0100
@@ -2103,6 +2103,8 @@
             result = res;
         } else if (sym.kind == VAR && sym.owner.kind == MTH) {
             result = items.makeLocalItem((VarSymbol)sym);
+        } else if (isInvokeDynamic(sym)) {
+            result = items.makeDynamicItem(sym);
         } else if ((sym.flags() & STATIC) != 0) {
             if (!isAccessSuper(env.enclMethod))
                 sym = binaryQualifier(sym, env.enclClass.type);
@@ -2152,8 +2154,12 @@
             result = items.
                 makeImmediateItem(sym.type, ((VarSymbol) sym).getConstValue());
         } else {
-            if (!accessSuper)
+            if (isInvokeDynamic(sym)) {
+                result = items.makeDynamicItem(sym);
+                return;
+            } else if (!accessSuper) {
                 sym = binaryQualifier(sym, tree.selected.type);
+            }
             if ((sym.flags() & STATIC) != 0) {
                 if (!selectSuper && (ssym == null || ssym.kind != TYP))
                     base = base.load();
@@ -2174,6 +2180,10 @@
         }
     }
 
+    public boolean isInvokeDynamic(Symbol sym) {
+        return sym.kind == MTH && ((MethodSymbol)sym).isDynamic();
+    }
+
     public void visitLiteral(JCLiteral tree) {
         if (tree.type.tag == TypeTags.BOT) {
             code.emitop0(aconst_null);
--- a/src/share/classes/com/sun/tools/javac/jvm/Items.java	Tue Sep 25 11:52:37 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/Items.java	Tue Sep 25 11:53:18 2012 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -110,6 +110,13 @@
         return stackItem[Code.typecode(type)];
     }
 
+    /** Make an item representing a dynamically invoked method.
+     *  @param member   The represented symbol.
+     */
+    Item makeDynamicItem(Symbol member) {
+        return new DynamicItem(member);
+    }
+
     /** Make an item representing an indexed expression.
      *  @param type    The expression's type.
      */
@@ -457,6 +464,35 @@
         }
     }
 
+    /** An item representing a dynamic call site.
+     */
+    class DynamicItem extends StaticItem {
+        DynamicItem(Symbol member) {
+            super(member);
+        }
+
+        Item load() {
+            assert false;
+            return null;
+        }
+
+        void store() {
+            assert false;
+        }
+
+        Item invoke() {
+            // assert target.hasNativeInvokeDynamic();
+            MethodType mtype = (MethodType)member.erasure(types);
+            int rescode = Code.typecode(mtype.restype);
+            code.emitInvokedynamic(pool.put(member), mtype);
+            return stackItem[rescode];
+        }
+
+        public String toString() {
+            return "dynamic(" + member + ")";
+        }
+    }
+
     /** An item representing an instance variable or method.
      */
     class MemberItem extends Item {
--- a/src/share/classes/com/sun/tools/javac/jvm/Pool.java	Tue Sep 25 11:52:37 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/Pool.java	Tue Sep 25 11:53:18 2012 +0100
@@ -25,9 +25,17 @@
 
 package com.sun.tools.javac.jvm;
 
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Kinds;
 import java.util.*;
 
+import com.sun.tools.javac.code.Symbol;
 import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.util.Assert;
+import com.sun.tools.javac.util.Filter;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Names;
 
 /** An internal structure that corresponds to the constant pool of a classfile.
  *
@@ -167,4 +175,90 @@
                 v.type.hashCode();
         }
     }
+
+    public static class MethodHandle {
+
+        /** Reference kind - see ClassFile */
+        int refKind;
+
+        /** Reference symbol */
+        Symbol refSym;
+
+        /** Reference to the name table */
+        Names names;
+
+        public MethodHandle(int refKind, Symbol refSym, Names names) {
+            this.refKind = refKind;
+            this.refSym = refSym;
+            this.names = names;
+            checkConsistent();
+        }
+        public boolean equals(Object other) {
+            if (!(other instanceof MethodHandle)) return false;
+            MethodHandle mr = (MethodHandle) other;
+            if (mr.refKind != refKind)  return false;
+            Symbol o = mr.refSym;
+            return
+                o.name == refSym.name &&
+                o.owner == refSym.owner &&
+                o.type.equals(refSym.type);
+        }
+        public int hashCode() {
+            return
+                refKind * 65 +
+                refSym.name.hashCode() * 33 +
+                refSym.owner.hashCode() * 9 +
+                refSym.type.hashCode();
+        }
+
+        /**
+         * Check consistency of reference kind and symbol (see JVMS 4.4.8)
+         */
+        @SuppressWarnings("fallthrough")
+        private void checkConsistent() {
+            boolean staticOk = false;
+            int expectedKind = -1;
+            Filter<Name> nameFilter = nonInitFilter;
+            boolean interfaceOwner = false;
+            switch (refKind) {
+                case ClassFile.REF_getStatic:
+                case ClassFile.REF_putStatic:
+                    staticOk = true;
+                case ClassFile.REF_getField:
+                case ClassFile.REF_putField:
+                    expectedKind = Kinds.VAR;
+                    break;
+                case ClassFile.REF_newInvokeSpecial:
+                    nameFilter = initFilter;
+                    expectedKind = Kinds.MTH;
+                    break;
+                case ClassFile.REF_invokeInterface:
+                    interfaceOwner = true;
+                    expectedKind = Kinds.MTH;
+                    break;
+                case ClassFile.REF_invokeStatic:
+                    staticOk = true;
+                case ClassFile.REF_invokeVirtual:
+                case ClassFile.REF_invokeSpecial:
+                    expectedKind = Kinds.MTH;
+                    break;
+            }
+            Assert.check(!refSym.isStatic() || staticOk);
+            Assert.check(refSym.kind == expectedKind);
+            Assert.check(nameFilter.accepts(refSym.name));
+            Assert.check(!refSym.owner.isInterface() || interfaceOwner);
+        }
+        //where
+                Filter<Name> nonInitFilter = new Filter<Name>() {
+                    public boolean accepts(Name n) {
+                        return n != names.init && n != names.clinit;
+                    }
+                };
+
+                Filter<Name> initFilter = new Filter<Name>() {
+                    public boolean accepts(Name n) {
+                        return n == names.init;
+                    }
+                };
+    }
 }
--- a/src/share/classes/com/sun/tools/javac/util/Names.java	Tue Sep 25 11:52:37 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/util/Names.java	Tue Sep 25 11:53:18 2012 +0100
@@ -169,6 +169,9 @@
     public final Name ex;
     public final Name package_info;
 
+    // lambda-related
+    public final Name BootstrapMethods;
+
     public final Name.Table table;
 
     public Names(Context context) {
@@ -296,6 +299,9 @@
         deprecated = fromString("deprecated");
         ex = fromString("ex");
         package_info = fromString("package-info");
+
+        //lambda-related
+        BootstrapMethods = fromString("BootstrapMethods");
     }
 
     protected Name.Table createTable(Options options) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/TestInvokeDynamic.java	Tue Sep 25 11:53:18 2012 +0100
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7194586
+ *
+ * @summary Add back-end support for invokedynamic
+ *
+ */
+
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import com.sun.source.util.TreeScanner;
+
+import com.sun.tools.classfile.Attribute;
+import com.sun.tools.classfile.BootstrapMethods_attribute;
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.Code_attribute;
+import com.sun.tools.classfile.ConstantPool.*;
+import com.sun.tools.classfile.Instruction;
+import com.sun.tools.classfile.Method;
+
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.jvm.Pool;
+import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Names;
+
+import java.io.File;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Locale;
+
+import javax.tools.Diagnostic;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+import static com.sun.tools.javac.jvm.ClassFile.*;
+
+public class TestInvokeDynamic {
+
+    static int checkCount = 0;
+
+    enum StaticArgumentKind {
+        STRING("Hello!", "String", "Ljava/lang/String;") {
+            @Override
+            boolean check(CPInfo cpInfo) throws Exception {
+                return (cpInfo instanceof CONSTANT_String_info) &&
+                        ((CONSTANT_String_info)cpInfo).getString().equals(value);
+            }
+        },
+        CLASS(null, "Class<?>", "Ljava/lang/Class;") {
+            @Override
+            boolean check(CPInfo cpInfo) throws Exception {
+                return (cpInfo instanceof CONSTANT_Class_info) &&
+                        ((CONSTANT_Class_info)cpInfo).getName().equals("java/lang/String");
+            }
+        },
+        INTEGER(1, "int", "I") {
+            @Override
+            boolean check(CPInfo cpInfo) throws Exception {
+                return (cpInfo instanceof CONSTANT_Integer_info) &&
+                        ((CONSTANT_Integer_info)cpInfo).value == ((Integer)value).intValue();
+            }
+        },
+        LONG(1L, "long", "J") {
+            @Override
+            boolean check(CPInfo cpInfo) throws Exception {
+                return (cpInfo instanceof CONSTANT_Long_info) &&
+                        ((CONSTANT_Long_info)cpInfo).value == ((Long)value).longValue();
+            }
+        },
+        FLOAT(1.0f, "float", "F") {
+            @Override
+            boolean check(CPInfo cpInfo) throws Exception {
+                return (cpInfo instanceof CONSTANT_Float_info) &&
+                        ((CONSTANT_Float_info)cpInfo).value == ((Float)value).floatValue();
+            }
+        },
+        DOUBLE(1.0, "double","D") {
+            @Override
+            boolean check(CPInfo cpInfo) throws Exception {
+                return (cpInfo instanceof CONSTANT_Double_info) &&
+                        ((CONSTANT_Double_info)cpInfo).value == ((Double)value).doubleValue();
+            }
+        },
+        METHOD_HANDLE(null, "MethodHandle", "Ljava/lang/invoke/MethodHandle;") {
+            @Override
+            boolean check(CPInfo cpInfo) throws Exception {
+                if (!(cpInfo instanceof CONSTANT_MethodHandle_info)) return false;
+                CONSTANT_MethodHandle_info handleInfo = (CONSTANT_MethodHandle_info)cpInfo;
+                return handleInfo.getCPRefInfo().getClassName().equals("Array") &&
+                        handleInfo.reference_kind == RefKind.REF_invokeVirtual &&
+                        handleInfo.getCPRefInfo().getNameAndTypeInfo().getName().equals("clone") &&
+                        handleInfo.getCPRefInfo().getNameAndTypeInfo().getType().equals("()Ljava/lang/Object;");
+            }
+        },
+        METHOD_TYPE(null, "MethodType", "Ljava/lang/invoke/MethodType;") {
+            @Override
+            boolean check(CPInfo cpInfo) throws Exception {
+                return (cpInfo instanceof CONSTANT_MethodType_info) &&
+                        ((CONSTANT_MethodType_info)cpInfo).getType().equals("()Ljava/lang/Object;");
+            }
+        };
+
+        Object value;
+        String sourceTypeStr;
+        String bytecodeTypeStr;
+
+        StaticArgumentKind(Object value, String sourceTypeStr, String bytecodeTypeStr) {
+            this.value = value;
+            this.sourceTypeStr = sourceTypeStr;
+            this.bytecodeTypeStr = bytecodeTypeStr;
+        }
+
+        abstract boolean check(CPInfo cpInfo) throws Exception;
+
+        Object getValue(Symtab syms, Names names) {
+            switch (this) {
+                case STRING:
+                case INTEGER:
+                case LONG:
+                case FLOAT:
+                case DOUBLE:
+                    return value;
+                case CLASS:
+                    return syms.stringType.tsym;
+                case METHOD_HANDLE:
+                    return new Pool.MethodHandle(REF_invokeVirtual, syms.arrayCloneMethod, names);
+                case METHOD_TYPE:
+                    return syms.arrayCloneMethod.type;
+                default:
+                    throw new AssertionError();
+            }
+        }
+    }
+
+    enum StaticArgumentsArity {
+        ZERO(0),
+        ONE(1),
+        TWO(2),
+        THREE(3);
+
+        int arity;
+
+        StaticArgumentsArity(int arity) {
+            this.arity = arity;
+        }
+    }
+
+    public static void main(String... args) throws Exception {
+        // Create a single file manager and compiler and reuse it for each compile to save time.
+        StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
+        final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
+        for (StaticArgumentsArity arity : StaticArgumentsArity.values()) {
+            if (arity.arity == 0) {
+                new TestInvokeDynamic(arity).compileAndCheck(fm, tool);
+            } else {
+                for (StaticArgumentKind sak1 : StaticArgumentKind.values()) {
+                    if (arity.arity == 1) {
+                        new TestInvokeDynamic(arity, sak1).compileAndCheck(fm, tool);
+                    } else {
+                        for (StaticArgumentKind sak2 : StaticArgumentKind.values()) {
+                            if (arity.arity == 2) {
+                                new TestInvokeDynamic(arity, sak1, sak2).compileAndCheck(fm, tool);
+                            } else {
+                                for (StaticArgumentKind sak3 : StaticArgumentKind.values()) {
+                                    new TestInvokeDynamic(arity, sak1, sak2, sak3).compileAndCheck(fm, tool);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        System.out.println("Total checks made: " + checkCount);
+    }
+
+    StaticArgumentsArity arity;
+    StaticArgumentKind[] saks;
+    JavaSource source;
+    DiagChecker dc;
+
+    TestInvokeDynamic(StaticArgumentsArity arity, StaticArgumentKind... saks) {
+        this.arity = arity;
+        this.saks = saks;
+        source = new JavaSource();
+        dc = new DiagChecker();
+    }
+
+    void compileAndCheck(JavaFileManager fm, JavaCompiler tool) throws Exception {
+        JavacTaskImpl ct = (JavacTaskImpl)tool.getTask(null, fm, dc,
+                null, null, Arrays.asList(source));
+        Context context = ct.getContext();
+        Symtab syms = Symtab.instance(context);
+        Names names = Names.instance(context);
+        ct.addTaskListener(new Indifier(syms, names));
+        try {
+            ct.generate();
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw new AssertionError(String.format("Error thrown when compiling following code\n%s", source.source));
+        }
+        if (dc.diagFound) {
+            throw new AssertionError(String.format("Diags found when compiling following code\n%s\n\n%s", source.source, dc.printDiags()));
+        }
+        verifyBytecode();
+    }
+
+    void verifyBytecode() {
+        File compiledTest = new File("Test.class");
+        try {
+            ClassFile cf = ClassFile.read(compiledTest);
+            Method testMethod = null;
+            for (Method m : cf.methods) {
+                if (m.getName(cf.constant_pool).equals("test")) {
+                    testMethod = m;
+                    break;
+                }
+            }
+            if (testMethod == null) {
+                throw new Error("Test method not found");
+            }
+            Code_attribute ea = (Code_attribute)testMethod.attributes.get(Attribute.Code);
+            if (testMethod == null) {
+                throw new Error("Code attribute for test() method not found");
+            }
+
+            int bsmIdx = -1;
+
+            for (Instruction i : ea.getInstructions()) {
+                if (i.getMnemonic().equals("invokedynamic")) {
+                    CONSTANT_InvokeDynamic_info indyInfo =
+                            (CONSTANT_InvokeDynamic_info)cf.constant_pool.get(i.getShort(1));
+                    bsmIdx = indyInfo.bootstrap_method_attr_index;
+                    if (!indyInfo.getNameAndTypeInfo().getType().equals("()V")) {
+                        throw new AssertionError("type mismatch for CONSTANT_InvokeDynamic_info");
+                    }
+                }
+            }
+            if (bsmIdx == -1) {
+                throw new Error("Missing invokedynamic in generated code");
+            }
+
+            BootstrapMethods_attribute bsm_attr = (BootstrapMethods_attribute)cf.getAttribute(Attribute.BootstrapMethods);
+            if (bsm_attr.bootstrap_method_specifiers.length != 1) {
+                throw new Error("Bad number of method specifiers in BootstrapMethods attribute");
+            }
+            BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec =
+                    bsm_attr.bootstrap_method_specifiers[0];
+
+            if (bsm_spec.bootstrap_arguments.length != arity.arity) {
+                throw new Error("Bad number of static invokedynamic args in BootstrapMethod attribute");
+            }
+
+            int count = 0;
+            for (StaticArgumentKind sak : saks) {
+                if (!sak.check(cf.constant_pool.get(bsm_spec.bootstrap_arguments[count]))) {
+                    throw new Error("Bad static argument value " + sak);
+                }
+                count++;
+            }
+
+            CONSTANT_MethodHandle_info bsm_handle =
+                    (CONSTANT_MethodHandle_info)cf.constant_pool.get(bsm_spec.bootstrap_method_ref);
+
+            if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) {
+                throw new Error("Bad kind on boostrap method handle");
+            }
+
+            CONSTANT_Methodref_info bsm_ref =
+                    (CONSTANT_Methodref_info)cf.constant_pool.get(bsm_handle.reference_index);
+
+            if (!bsm_ref.getClassInfo().getName().equals("Bootstrap")) {
+                throw new Error("Bad owner of boostrap method");
+            }
+
+            if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) {
+                throw new Error("Bad boostrap method name");
+            }
+
+            if (!bsm_ref.getNameAndTypeInfo().getType().equals(asBSMSignatureString())) {
+                throw new Error("Bad boostrap method type" + bsm_ref.getNameAndTypeInfo().getType() + " " + asBSMSignatureString());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new Error("error reading " + compiledTest +": " + e);
+        }
+    }
+
+    String asBSMSignatureString() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;");
+        for (StaticArgumentKind sak : saks) {
+            buf.append(sak.bytecodeTypeStr);
+        }
+        buf.append(")Ljava/lang/invoke/CallSite;");
+        return buf.toString();
+    }
+
+    class JavaSource extends SimpleJavaFileObject {
+
+        static final String source_template = "import java.lang.invoke.*;\n" +
+                "class Bootstrap {\n" +
+                "   public static CallSite bsm(MethodHandles.Lookup lookup, String name, MethodType methodType #SARGS) {\n" +
+                "       return null;\n" +
+                "   }\n" +
+                "}\n" +
+                "class Test {\n" +
+                "   void m() { }\n" +
+                "   void test() { m(); }\n" +
+                "}";
+
+        String source;
+
+        JavaSource() {
+            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+            source = source_template.replace("#SARGS", asSignatureString());
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return source;
+        }
+
+        String asSignatureString() {
+            int count = 0;
+            StringBuilder buf = new StringBuilder();
+            for (StaticArgumentKind sak : saks) {
+                buf.append(",");
+                buf.append(sak.sourceTypeStr);
+                buf.append(' ');
+                buf.append(String.format("x%d", count++));
+            }
+            return buf.toString();
+        }
+    }
+
+    class Indifier extends TreeScanner<Void, Void> implements TaskListener {
+
+        MethodSymbol bsm;
+        Symtab syms;
+        Names names;
+
+        Indifier(Symtab syms, Names names) {
+            this.syms = syms;
+            this.names = names;
+        }
+
+        @Override
+        public void started(TaskEvent e) {
+            //do nothing
+        }
+
+        @Override
+        public void finished(TaskEvent e) {
+            if (e.getKind() == TaskEvent.Kind.ANALYZE) {
+                scan(e.getCompilationUnit(), null);
+            }
+        }
+
+        @Override
+        public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
+            super.visitMethodInvocation(node, p);
+            JCMethodInvocation apply = (JCMethodInvocation)node;
+            JCIdent ident = (JCIdent)apply.meth;
+            Symbol oldSym = ident.sym;
+            if (!oldSym.isConstructor()) {
+                Object[] staticArgs = new Object[arity.arity];
+                for (int i = 0; i < arity.arity ; i++) {
+                    staticArgs[i] = saks[i].getValue(syms, names);
+                }
+                ident.sym = new Symbol.DynamicMethodSymbol(oldSym.name, oldSym.owner, REF_invokeStatic, bsm, oldSym.type, staticArgs);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visitMethod(MethodTree node, Void p) {
+            super.visitMethod(node, p);
+            if (node.getName().toString().equals("bsm")) {
+                bsm = ((JCMethodDecl)node).sym;
+            }
+            return null;
+        }
+    }
+
+    static class DiagChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
+
+        boolean diagFound;
+        ArrayList<String> diags = new ArrayList<>();
+
+        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+            diags.add(diagnostic.getMessage(Locale.getDefault()));
+            diagFound = true;
+        }
+
+        String printDiags() {
+            StringBuilder buf = new StringBuilder();
+            for (String s : diags) {
+                buf.append(s);
+                buf.append("\n");
+            }
+            return buf.toString();
+        }
+    }
+}