7194586: Add back-end support for invokedynamic

Tue, 25 Sep 2012 11:53:18 +0100

author
mcimadamore
date
Tue, 25 Sep 2012 11:53:18 +0100
changeset 1336
26d93df3905a
parent 1335
99983a4a593b
child 1337
2eca84194807

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

src/share/classes/com/sun/tools/javac/code/Symbol.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/code/Symtab.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/jvm/ClassFile.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/jvm/Code.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/jvm/Gen.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/jvm/Items.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/jvm/Pool.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/util/Names.java file | annotate | diff | comparison | revisions
test/tools/javac/lambda/TestInvokeDynamic.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Sep 25 11:52:37 2012 +0100
     1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Sep 25 11:53:18 2012 +0100
     1.3 @@ -1068,6 +1068,10 @@
     1.4              }
     1.5          }
     1.6  
     1.7 +        public boolean isDynamic() {
     1.8 +            return false;
     1.9 +        }
    1.10 +
    1.11          /** find a symbol that this (proxy method) symbol implements.
    1.12           *  @param    c       The class whose members are searched for
    1.13           *                    implementations
    1.14 @@ -1356,6 +1360,27 @@
    1.15          }
    1.16      }
    1.17  
    1.18 +    /** A class for invokedynamic method calls.
    1.19 +     */
    1.20 +    public static class DynamicMethodSymbol extends MethodSymbol {
    1.21 +
    1.22 +        public Object[] staticArgs;
    1.23 +        public Symbol bsm;
    1.24 +        public int bsmKind;
    1.25 +
    1.26 +        public DynamicMethodSymbol(Name name, Symbol owner, int bsmKind, MethodSymbol bsm, Type type, Object[] staticArgs) {
    1.27 +            super(0, name, type, owner);
    1.28 +            this.bsm = bsm;
    1.29 +            this.bsmKind = bsmKind;
    1.30 +            this.staticArgs = staticArgs;
    1.31 +        }
    1.32 +
    1.33 +        @Override
    1.34 +        public boolean isDynamic() {
    1.35 +            return true;
    1.36 +        }
    1.37 +    }
    1.38 +
    1.39      /** A class for predefined operators.
    1.40       */
    1.41      public static class OperatorSymbol extends MethodSymbol {
     2.1 --- a/src/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Sep 25 11:52:37 2012 +0100
     2.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Sep 25 11:53:18 2012 +0100
     2.3 @@ -126,6 +126,7 @@
     2.4      public final Type cloneableType;
     2.5      public final Type serializableType;
     2.6      public final Type methodHandleType;
     2.7 +    public final Type methodTypeType;
     2.8      public final Type nativeHeaderType;
     2.9      public final Type throwableType;
    2.10      public final Type errorType;
    2.11 @@ -440,6 +441,7 @@
    2.12          throwableType = enterClass("java.lang.Throwable");
    2.13          serializableType = enterClass("java.io.Serializable");
    2.14          methodHandleType = enterClass("java.lang.invoke.MethodHandle");
    2.15 +        methodTypeType = enterClass("java.lang.invoke.MethodType");
    2.16          errorType = enterClass("java.lang.Error");
    2.17          illegalArgumentExceptionType = enterClass("java.lang.IllegalArgumentException");
    2.18          interruptedExceptionType = enterClass("java.lang.InterruptedException");
     3.1 --- a/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java	Tue Sep 25 11:52:37 2012 +0100
     3.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java	Tue Sep 25 11:53:18 2012 +0100
     3.3 @@ -84,6 +84,16 @@
     3.4      public final static int CONSTANT_MethodType = 16;
     3.5      public final static int CONSTANT_InvokeDynamic = 18;
     3.6  
     3.7 +    public final static int REF_getField = 1;
     3.8 +    public final static int REF_getStatic = 2;
     3.9 +    public final static int REF_putField = 3;
    3.10 +    public final static int REF_putStatic = 4;
    3.11 +    public final static int REF_invokeVirtual = 5;
    3.12 +    public final static int REF_invokeStatic = 6;
    3.13 +    public final static int REF_invokeSpecial = 7;
    3.14 +    public final static int REF_newInvokeSpecial = 8;
    3.15 +    public final static int REF_invokeInterface = 9;
    3.16 +
    3.17      public final static int MAX_PARAMETERS = 0xff;
    3.18      public final static int MAX_DIMENSIONS = 0xff;
    3.19      public final static int MAX_CODE = 0xffff;
     4.1 --- a/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Tue Sep 25 11:52:37 2012 +0100
     4.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Tue Sep 25 11:53:18 2012 +0100
     4.3 @@ -26,6 +26,8 @@
     4.4  package com.sun.tools.javac.jvm;
     4.5  
     4.6  import java.io.*;
     4.7 +import java.util.LinkedHashMap;
     4.8 +import java.util.Map;
     4.9  import java.util.Set;
    4.10  import java.util.HashSet;
    4.11  
    4.12 @@ -137,6 +139,11 @@
    4.13       */
    4.14      ListBuffer<ClassSymbol> innerClassesQueue;
    4.15  
    4.16 +    /** The bootstrap methods to be written in the corresponding class attribute
    4.17 +     *  (one for each invokedynamic)
    4.18 +     */
    4.19 +    Map<MethodSymbol, Pool.MethodHandle> bootstrapMethods;
    4.20 +
    4.21      /** The log to use for verbose output.
    4.22       */
    4.23      private final Log log;
    4.24 @@ -477,11 +484,27 @@
    4.25  
    4.26              if (value instanceof MethodSymbol) {
    4.27                  MethodSymbol m = (MethodSymbol)value;
    4.28 -                poolbuf.appendByte((m.owner.flags() & INTERFACE) != 0
    4.29 -                          ? CONSTANT_InterfaceMethodref
    4.30 -                          : CONSTANT_Methodref);
    4.31 -                poolbuf.appendChar(pool.put(m.owner));
    4.32 -                poolbuf.appendChar(pool.put(nameType(m)));
    4.33 +                if (!m.isDynamic()) {
    4.34 +                    poolbuf.appendByte((m.owner.flags() & INTERFACE) != 0
    4.35 +                              ? CONSTANT_InterfaceMethodref
    4.36 +                              : CONSTANT_Methodref);
    4.37 +                    poolbuf.appendChar(pool.put(m.owner));
    4.38 +                    poolbuf.appendChar(pool.put(nameType(m)));
    4.39 +                } else {
    4.40 +                    //invokedynamic
    4.41 +                    DynamicMethodSymbol dynSym = (DynamicMethodSymbol)m;
    4.42 +                    Pool.MethodHandle handle = new Pool.MethodHandle(dynSym.bsmKind, dynSym.bsm, names);
    4.43 +                    bootstrapMethods.put(dynSym, handle);
    4.44 +                    //init cp entries
    4.45 +                    pool.put(names.BootstrapMethods);
    4.46 +                    pool.put(handle);
    4.47 +                    for (Object staticArg : dynSym.staticArgs) {
    4.48 +                        pool.put(staticArg);
    4.49 +                    }
    4.50 +                    poolbuf.appendByte(CONSTANT_InvokeDynamic);
    4.51 +                    poolbuf.appendChar(bootstrapMethods.size() - 1);
    4.52 +                    poolbuf.appendChar(pool.put(nameType(dynSym)));
    4.53 +                }
    4.54              } else if (value instanceof VarSymbol) {
    4.55                  VarSymbol v = (VarSymbol)value;
    4.56                  poolbuf.appendByte(CONSTANT_Fieldref);
    4.57 @@ -526,11 +549,20 @@
    4.58              } else if (value instanceof String) {
    4.59                  poolbuf.appendByte(CONSTANT_String);
    4.60                  poolbuf.appendChar(pool.put(names.fromString((String)value)));
    4.61 +            } else if (value instanceof MethodType) {
    4.62 +                MethodType mtype = (MethodType)value;
    4.63 +                poolbuf.appendByte(CONSTANT_MethodType);
    4.64 +                poolbuf.appendChar(pool.put(typeSig(mtype)));
    4.65              } else if (value instanceof Type) {
    4.66                  Type type = (Type)value;
    4.67                  if (type.tag == CLASS) enterInner((ClassSymbol)type.tsym);
    4.68                  poolbuf.appendByte(CONSTANT_Class);
    4.69                  poolbuf.appendChar(pool.put(xClassName(type)));
    4.70 +            } else if (value instanceof Pool.MethodHandle) {
    4.71 +                Pool.MethodHandle ref = (Pool.MethodHandle)value;
    4.72 +                poolbuf.appendByte(CONSTANT_MethodHandle);
    4.73 +                poolbuf.appendByte(ref.refKind);
    4.74 +                poolbuf.appendChar(pool.put(ref.refSym));
    4.75              } else {
    4.76                  Assert.error("writePool " + value);
    4.77              }
    4.78 @@ -914,6 +946,25 @@
    4.79          endAttr(alenIdx);
    4.80      }
    4.81  
    4.82 +    /** Write "bootstrapMethods" attribute.
    4.83 +     */
    4.84 +    void writeBootstrapMethods() {
    4.85 +        int alenIdx = writeAttr(names.BootstrapMethods);
    4.86 +        databuf.appendChar(bootstrapMethods.size());
    4.87 +        for (Map.Entry<MethodSymbol, Pool.MethodHandle> entry : bootstrapMethods.entrySet()) {
    4.88 +            DynamicMethodSymbol dsym = (DynamicMethodSymbol)entry.getKey();
    4.89 +            //write BSM handle
    4.90 +            databuf.appendChar(pool.get(entry.getValue()));
    4.91 +            //write static args length
    4.92 +            databuf.appendChar(dsym.staticArgs.length);
    4.93 +            //write static args array
    4.94 +            for (Object o : dsym.staticArgs) {
    4.95 +                databuf.appendChar(pool.get(o));
    4.96 +            }
    4.97 +        }
    4.98 +        endAttr(alenIdx);
    4.99 +    }
   4.100 +
   4.101      /** Write field symbol, entering all references into constant pool.
   4.102       */
   4.103      void writeField(VarSymbol v) {
   4.104 @@ -1483,6 +1534,7 @@
   4.105          pool = c.pool;
   4.106          innerClasses = null;
   4.107          innerClassesQueue = null;
   4.108 +        bootstrapMethods = new LinkedHashMap<MethodSymbol, Pool.MethodHandle>();
   4.109  
   4.110          Type supertype = types.supertype(c.type);
   4.111          List<Type> interfaces = types.interfaces(c.type);
   4.112 @@ -1589,6 +1641,12 @@
   4.113              writeInnerClasses();
   4.114              acount++;
   4.115          }
   4.116 +
   4.117 +        if (!bootstrapMethods.isEmpty()) {
   4.118 +            writeBootstrapMethods();
   4.119 +            acount++;
   4.120 +        }
   4.121 +
   4.122          endAttrs(acountIdx, acount);
   4.123  
   4.124          poolbuf.appendBytes(databuf.elems, 0, databuf.length);
     5.1 --- a/src/share/classes/com/sun/tools/javac/jvm/Code.java	Tue Sep 25 11:52:37 2012 +0100
     5.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/Code.java	Tue Sep 25 11:53:18 2012 +0100
     5.3 @@ -903,6 +903,8 @@
     5.4          if (o instanceof Double) return syms.doubleType;
     5.5          if (o instanceof ClassSymbol) return syms.classType;
     5.6          if (o instanceof Type.ArrayType) return syms.classType;
     5.7 +        if (o instanceof Type.MethodType) return syms.methodTypeType;
     5.8 +        if (o instanceof Pool.MethodHandle) return syms.methodHandleType;
     5.9          throw new AssertionError(o);
    5.10      }
    5.11  
     6.1 --- a/src/share/classes/com/sun/tools/javac/jvm/Gen.java	Tue Sep 25 11:52:37 2012 +0100
     6.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/Gen.java	Tue Sep 25 11:53:18 2012 +0100
     6.3 @@ -2103,6 +2103,8 @@
     6.4              result = res;
     6.5          } else if (sym.kind == VAR && sym.owner.kind == MTH) {
     6.6              result = items.makeLocalItem((VarSymbol)sym);
     6.7 +        } else if (isInvokeDynamic(sym)) {
     6.8 +            result = items.makeDynamicItem(sym);
     6.9          } else if ((sym.flags() & STATIC) != 0) {
    6.10              if (!isAccessSuper(env.enclMethod))
    6.11                  sym = binaryQualifier(sym, env.enclClass.type);
    6.12 @@ -2152,8 +2154,12 @@
    6.13              result = items.
    6.14                  makeImmediateItem(sym.type, ((VarSymbol) sym).getConstValue());
    6.15          } else {
    6.16 -            if (!accessSuper)
    6.17 +            if (isInvokeDynamic(sym)) {
    6.18 +                result = items.makeDynamicItem(sym);
    6.19 +                return;
    6.20 +            } else if (!accessSuper) {
    6.21                  sym = binaryQualifier(sym, tree.selected.type);
    6.22 +            }
    6.23              if ((sym.flags() & STATIC) != 0) {
    6.24                  if (!selectSuper && (ssym == null || ssym.kind != TYP))
    6.25                      base = base.load();
    6.26 @@ -2174,6 +2180,10 @@
    6.27          }
    6.28      }
    6.29  
    6.30 +    public boolean isInvokeDynamic(Symbol sym) {
    6.31 +        return sym.kind == MTH && ((MethodSymbol)sym).isDynamic();
    6.32 +    }
    6.33 +
    6.34      public void visitLiteral(JCLiteral tree) {
    6.35          if (tree.type.tag == TypeTags.BOT) {
    6.36              code.emitop0(aconst_null);
     7.1 --- a/src/share/classes/com/sun/tools/javac/jvm/Items.java	Tue Sep 25 11:52:37 2012 +0100
     7.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/Items.java	Tue Sep 25 11:53:18 2012 +0100
     7.3 @@ -1,5 +1,5 @@
     7.4  /*
     7.5 - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
     7.6 + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
     7.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     7.8   *
     7.9   * This code is free software; you can redistribute it and/or modify it
    7.10 @@ -110,6 +110,13 @@
    7.11          return stackItem[Code.typecode(type)];
    7.12      }
    7.13  
    7.14 +    /** Make an item representing a dynamically invoked method.
    7.15 +     *  @param member   The represented symbol.
    7.16 +     */
    7.17 +    Item makeDynamicItem(Symbol member) {
    7.18 +        return new DynamicItem(member);
    7.19 +    }
    7.20 +
    7.21      /** Make an item representing an indexed expression.
    7.22       *  @param type    The expression's type.
    7.23       */
    7.24 @@ -457,6 +464,35 @@
    7.25          }
    7.26      }
    7.27  
    7.28 +    /** An item representing a dynamic call site.
    7.29 +     */
    7.30 +    class DynamicItem extends StaticItem {
    7.31 +        DynamicItem(Symbol member) {
    7.32 +            super(member);
    7.33 +        }
    7.34 +
    7.35 +        Item load() {
    7.36 +            assert false;
    7.37 +            return null;
    7.38 +        }
    7.39 +
    7.40 +        void store() {
    7.41 +            assert false;
    7.42 +        }
    7.43 +
    7.44 +        Item invoke() {
    7.45 +            // assert target.hasNativeInvokeDynamic();
    7.46 +            MethodType mtype = (MethodType)member.erasure(types);
    7.47 +            int rescode = Code.typecode(mtype.restype);
    7.48 +            code.emitInvokedynamic(pool.put(member), mtype);
    7.49 +            return stackItem[rescode];
    7.50 +        }
    7.51 +
    7.52 +        public String toString() {
    7.53 +            return "dynamic(" + member + ")";
    7.54 +        }
    7.55 +    }
    7.56 +
    7.57      /** An item representing an instance variable or method.
    7.58       */
    7.59      class MemberItem extends Item {
     8.1 --- a/src/share/classes/com/sun/tools/javac/jvm/Pool.java	Tue Sep 25 11:52:37 2012 +0100
     8.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/Pool.java	Tue Sep 25 11:53:18 2012 +0100
     8.3 @@ -25,9 +25,17 @@
     8.4  
     8.5  package com.sun.tools.javac.jvm;
     8.6  
     8.7 +import com.sun.tools.javac.code.Flags;
     8.8 +import com.sun.tools.javac.code.Kinds;
     8.9  import java.util.*;
    8.10  
    8.11 +import com.sun.tools.javac.code.Symbol;
    8.12  import com.sun.tools.javac.code.Symbol.*;
    8.13 +import com.sun.tools.javac.code.Type;
    8.14 +import com.sun.tools.javac.util.Assert;
    8.15 +import com.sun.tools.javac.util.Filter;
    8.16 +import com.sun.tools.javac.util.Name;
    8.17 +import com.sun.tools.javac.util.Names;
    8.18  
    8.19  /** An internal structure that corresponds to the constant pool of a classfile.
    8.20   *
    8.21 @@ -167,4 +175,90 @@
    8.22                  v.type.hashCode();
    8.23          }
    8.24      }
    8.25 +
    8.26 +    public static class MethodHandle {
    8.27 +
    8.28 +        /** Reference kind - see ClassFile */
    8.29 +        int refKind;
    8.30 +
    8.31 +        /** Reference symbol */
    8.32 +        Symbol refSym;
    8.33 +
    8.34 +        /** Reference to the name table */
    8.35 +        Names names;
    8.36 +
    8.37 +        public MethodHandle(int refKind, Symbol refSym, Names names) {
    8.38 +            this.refKind = refKind;
    8.39 +            this.refSym = refSym;
    8.40 +            this.names = names;
    8.41 +            checkConsistent();
    8.42 +        }
    8.43 +        public boolean equals(Object other) {
    8.44 +            if (!(other instanceof MethodHandle)) return false;
    8.45 +            MethodHandle mr = (MethodHandle) other;
    8.46 +            if (mr.refKind != refKind)  return false;
    8.47 +            Symbol o = mr.refSym;
    8.48 +            return
    8.49 +                o.name == refSym.name &&
    8.50 +                o.owner == refSym.owner &&
    8.51 +                o.type.equals(refSym.type);
    8.52 +        }
    8.53 +        public int hashCode() {
    8.54 +            return
    8.55 +                refKind * 65 +
    8.56 +                refSym.name.hashCode() * 33 +
    8.57 +                refSym.owner.hashCode() * 9 +
    8.58 +                refSym.type.hashCode();
    8.59 +        }
    8.60 +
    8.61 +        /**
    8.62 +         * Check consistency of reference kind and symbol (see JVMS 4.4.8)
    8.63 +         */
    8.64 +        @SuppressWarnings("fallthrough")
    8.65 +        private void checkConsistent() {
    8.66 +            boolean staticOk = false;
    8.67 +            int expectedKind = -1;
    8.68 +            Filter<Name> nameFilter = nonInitFilter;
    8.69 +            boolean interfaceOwner = false;
    8.70 +            switch (refKind) {
    8.71 +                case ClassFile.REF_getStatic:
    8.72 +                case ClassFile.REF_putStatic:
    8.73 +                    staticOk = true;
    8.74 +                case ClassFile.REF_getField:
    8.75 +                case ClassFile.REF_putField:
    8.76 +                    expectedKind = Kinds.VAR;
    8.77 +                    break;
    8.78 +                case ClassFile.REF_newInvokeSpecial:
    8.79 +                    nameFilter = initFilter;
    8.80 +                    expectedKind = Kinds.MTH;
    8.81 +                    break;
    8.82 +                case ClassFile.REF_invokeInterface:
    8.83 +                    interfaceOwner = true;
    8.84 +                    expectedKind = Kinds.MTH;
    8.85 +                    break;
    8.86 +                case ClassFile.REF_invokeStatic:
    8.87 +                    staticOk = true;
    8.88 +                case ClassFile.REF_invokeVirtual:
    8.89 +                case ClassFile.REF_invokeSpecial:
    8.90 +                    expectedKind = Kinds.MTH;
    8.91 +                    break;
    8.92 +            }
    8.93 +            Assert.check(!refSym.isStatic() || staticOk);
    8.94 +            Assert.check(refSym.kind == expectedKind);
    8.95 +            Assert.check(nameFilter.accepts(refSym.name));
    8.96 +            Assert.check(!refSym.owner.isInterface() || interfaceOwner);
    8.97 +        }
    8.98 +        //where
    8.99 +                Filter<Name> nonInitFilter = new Filter<Name>() {
   8.100 +                    public boolean accepts(Name n) {
   8.101 +                        return n != names.init && n != names.clinit;
   8.102 +                    }
   8.103 +                };
   8.104 +
   8.105 +                Filter<Name> initFilter = new Filter<Name>() {
   8.106 +                    public boolean accepts(Name n) {
   8.107 +                        return n == names.init;
   8.108 +                    }
   8.109 +                };
   8.110 +    }
   8.111  }
     9.1 --- a/src/share/classes/com/sun/tools/javac/util/Names.java	Tue Sep 25 11:52:37 2012 +0100
     9.2 +++ b/src/share/classes/com/sun/tools/javac/util/Names.java	Tue Sep 25 11:53:18 2012 +0100
     9.3 @@ -169,6 +169,9 @@
     9.4      public final Name ex;
     9.5      public final Name package_info;
     9.6  
     9.7 +    // lambda-related
     9.8 +    public final Name BootstrapMethods;
     9.9 +
    9.10      public final Name.Table table;
    9.11  
    9.12      public Names(Context context) {
    9.13 @@ -296,6 +299,9 @@
    9.14          deprecated = fromString("deprecated");
    9.15          ex = fromString("ex");
    9.16          package_info = fromString("package-info");
    9.17 +
    9.18 +        //lambda-related
    9.19 +        BootstrapMethods = fromString("BootstrapMethods");
    9.20      }
    9.21  
    9.22      protected Name.Table createTable(Options options) {
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/test/tools/javac/lambda/TestInvokeDynamic.java	Tue Sep 25 11:53:18 2012 +0100
    10.3 @@ -0,0 +1,442 @@
    10.4 +/*
    10.5 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
    10.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    10.7 + *
    10.8 + * This code is free software; you can redistribute it and/or modify it
    10.9 + * under the terms of the GNU General Public License version 2 only, as
   10.10 + * published by the Free Software Foundation.
   10.11 + *
   10.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   10.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   10.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   10.15 + * version 2 for more details (a copy is included in the LICENSE file that
   10.16 + * accompanied this code).
   10.17 + *
   10.18 + * You should have received a copy of the GNU General Public License version
   10.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   10.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   10.21 + *
   10.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   10.23 + * or visit www.oracle.com if you need additional information or have any
   10.24 + * questions.
   10.25 + */
   10.26 +
   10.27 +/*
   10.28 + * @test
   10.29 + * @bug 7194586
   10.30 + *
   10.31 + * @summary Add back-end support for invokedynamic
   10.32 + *
   10.33 + */
   10.34 +
   10.35 +import com.sun.source.tree.MethodInvocationTree;
   10.36 +import com.sun.source.tree.MethodTree;
   10.37 +import com.sun.source.util.TaskEvent;
   10.38 +import com.sun.source.util.TaskListener;
   10.39 +import com.sun.source.util.TreeScanner;
   10.40 +
   10.41 +import com.sun.tools.classfile.Attribute;
   10.42 +import com.sun.tools.classfile.BootstrapMethods_attribute;
   10.43 +import com.sun.tools.classfile.ClassFile;
   10.44 +import com.sun.tools.classfile.Code_attribute;
   10.45 +import com.sun.tools.classfile.ConstantPool.*;
   10.46 +import com.sun.tools.classfile.Instruction;
   10.47 +import com.sun.tools.classfile.Method;
   10.48 +
   10.49 +import com.sun.tools.javac.api.JavacTaskImpl;
   10.50 +import com.sun.tools.javac.api.JavacTool;
   10.51 +import com.sun.tools.javac.code.Symbol;
   10.52 +import com.sun.tools.javac.code.Symbol.MethodSymbol;
   10.53 +import com.sun.tools.javac.code.Symtab;
   10.54 +import com.sun.tools.javac.jvm.Pool;
   10.55 +import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
   10.56 +import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
   10.57 +import com.sun.tools.javac.tree.JCTree.JCIdent;
   10.58 +import com.sun.tools.javac.util.Context;
   10.59 +import com.sun.tools.javac.util.Names;
   10.60 +
   10.61 +import java.io.File;
   10.62 +import java.net.URI;
   10.63 +import java.util.ArrayList;
   10.64 +import java.util.Arrays;
   10.65 +import java.util.Locale;
   10.66 +
   10.67 +import javax.tools.Diagnostic;
   10.68 +import javax.tools.JavaCompiler;
   10.69 +import javax.tools.JavaFileManager;
   10.70 +import javax.tools.JavaFileObject;
   10.71 +import javax.tools.SimpleJavaFileObject;
   10.72 +import javax.tools.StandardJavaFileManager;
   10.73 +import javax.tools.ToolProvider;
   10.74 +
   10.75 +import static com.sun.tools.javac.jvm.ClassFile.*;
   10.76 +
   10.77 +public class TestInvokeDynamic {
   10.78 +
   10.79 +    static int checkCount = 0;
   10.80 +
   10.81 +    enum StaticArgumentKind {
   10.82 +        STRING("Hello!", "String", "Ljava/lang/String;") {
   10.83 +            @Override
   10.84 +            boolean check(CPInfo cpInfo) throws Exception {
   10.85 +                return (cpInfo instanceof CONSTANT_String_info) &&
   10.86 +                        ((CONSTANT_String_info)cpInfo).getString().equals(value);
   10.87 +            }
   10.88 +        },
   10.89 +        CLASS(null, "Class<?>", "Ljava/lang/Class;") {
   10.90 +            @Override
   10.91 +            boolean check(CPInfo cpInfo) throws Exception {
   10.92 +                return (cpInfo instanceof CONSTANT_Class_info) &&
   10.93 +                        ((CONSTANT_Class_info)cpInfo).getName().equals("java/lang/String");
   10.94 +            }
   10.95 +        },
   10.96 +        INTEGER(1, "int", "I") {
   10.97 +            @Override
   10.98 +            boolean check(CPInfo cpInfo) throws Exception {
   10.99 +                return (cpInfo instanceof CONSTANT_Integer_info) &&
  10.100 +                        ((CONSTANT_Integer_info)cpInfo).value == ((Integer)value).intValue();
  10.101 +            }
  10.102 +        },
  10.103 +        LONG(1L, "long", "J") {
  10.104 +            @Override
  10.105 +            boolean check(CPInfo cpInfo) throws Exception {
  10.106 +                return (cpInfo instanceof CONSTANT_Long_info) &&
  10.107 +                        ((CONSTANT_Long_info)cpInfo).value == ((Long)value).longValue();
  10.108 +            }
  10.109 +        },
  10.110 +        FLOAT(1.0f, "float", "F") {
  10.111 +            @Override
  10.112 +            boolean check(CPInfo cpInfo) throws Exception {
  10.113 +                return (cpInfo instanceof CONSTANT_Float_info) &&
  10.114 +                        ((CONSTANT_Float_info)cpInfo).value == ((Float)value).floatValue();
  10.115 +            }
  10.116 +        },
  10.117 +        DOUBLE(1.0, "double","D") {
  10.118 +            @Override
  10.119 +            boolean check(CPInfo cpInfo) throws Exception {
  10.120 +                return (cpInfo instanceof CONSTANT_Double_info) &&
  10.121 +                        ((CONSTANT_Double_info)cpInfo).value == ((Double)value).doubleValue();
  10.122 +            }
  10.123 +        },
  10.124 +        METHOD_HANDLE(null, "MethodHandle", "Ljava/lang/invoke/MethodHandle;") {
  10.125 +            @Override
  10.126 +            boolean check(CPInfo cpInfo) throws Exception {
  10.127 +                if (!(cpInfo instanceof CONSTANT_MethodHandle_info)) return false;
  10.128 +                CONSTANT_MethodHandle_info handleInfo = (CONSTANT_MethodHandle_info)cpInfo;
  10.129 +                return handleInfo.getCPRefInfo().getClassName().equals("Array") &&
  10.130 +                        handleInfo.reference_kind == RefKind.REF_invokeVirtual &&
  10.131 +                        handleInfo.getCPRefInfo().getNameAndTypeInfo().getName().equals("clone") &&
  10.132 +                        handleInfo.getCPRefInfo().getNameAndTypeInfo().getType().equals("()Ljava/lang/Object;");
  10.133 +            }
  10.134 +        },
  10.135 +        METHOD_TYPE(null, "MethodType", "Ljava/lang/invoke/MethodType;") {
  10.136 +            @Override
  10.137 +            boolean check(CPInfo cpInfo) throws Exception {
  10.138 +                return (cpInfo instanceof CONSTANT_MethodType_info) &&
  10.139 +                        ((CONSTANT_MethodType_info)cpInfo).getType().equals("()Ljava/lang/Object;");
  10.140 +            }
  10.141 +        };
  10.142 +
  10.143 +        Object value;
  10.144 +        String sourceTypeStr;
  10.145 +        String bytecodeTypeStr;
  10.146 +
  10.147 +        StaticArgumentKind(Object value, String sourceTypeStr, String bytecodeTypeStr) {
  10.148 +            this.value = value;
  10.149 +            this.sourceTypeStr = sourceTypeStr;
  10.150 +            this.bytecodeTypeStr = bytecodeTypeStr;
  10.151 +        }
  10.152 +
  10.153 +        abstract boolean check(CPInfo cpInfo) throws Exception;
  10.154 +
  10.155 +        Object getValue(Symtab syms, Names names) {
  10.156 +            switch (this) {
  10.157 +                case STRING:
  10.158 +                case INTEGER:
  10.159 +                case LONG:
  10.160 +                case FLOAT:
  10.161 +                case DOUBLE:
  10.162 +                    return value;
  10.163 +                case CLASS:
  10.164 +                    return syms.stringType.tsym;
  10.165 +                case METHOD_HANDLE:
  10.166 +                    return new Pool.MethodHandle(REF_invokeVirtual, syms.arrayCloneMethod, names);
  10.167 +                case METHOD_TYPE:
  10.168 +                    return syms.arrayCloneMethod.type;
  10.169 +                default:
  10.170 +                    throw new AssertionError();
  10.171 +            }
  10.172 +        }
  10.173 +    }
  10.174 +
  10.175 +    enum StaticArgumentsArity {
  10.176 +        ZERO(0),
  10.177 +        ONE(1),
  10.178 +        TWO(2),
  10.179 +        THREE(3);
  10.180 +
  10.181 +        int arity;
  10.182 +
  10.183 +        StaticArgumentsArity(int arity) {
  10.184 +            this.arity = arity;
  10.185 +        }
  10.186 +    }
  10.187 +
  10.188 +    public static void main(String... args) throws Exception {
  10.189 +        // Create a single file manager and compiler and reuse it for each compile to save time.
  10.190 +        StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
  10.191 +        final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
  10.192 +        for (StaticArgumentsArity arity : StaticArgumentsArity.values()) {
  10.193 +            if (arity.arity == 0) {
  10.194 +                new TestInvokeDynamic(arity).compileAndCheck(fm, tool);
  10.195 +            } else {
  10.196 +                for (StaticArgumentKind sak1 : StaticArgumentKind.values()) {
  10.197 +                    if (arity.arity == 1) {
  10.198 +                        new TestInvokeDynamic(arity, sak1).compileAndCheck(fm, tool);
  10.199 +                    } else {
  10.200 +                        for (StaticArgumentKind sak2 : StaticArgumentKind.values()) {
  10.201 +                            if (arity.arity == 2) {
  10.202 +                                new TestInvokeDynamic(arity, sak1, sak2).compileAndCheck(fm, tool);
  10.203 +                            } else {
  10.204 +                                for (StaticArgumentKind sak3 : StaticArgumentKind.values()) {
  10.205 +                                    new TestInvokeDynamic(arity, sak1, sak2, sak3).compileAndCheck(fm, tool);
  10.206 +                                }
  10.207 +                            }
  10.208 +                        }
  10.209 +                    }
  10.210 +                }
  10.211 +            }
  10.212 +        }
  10.213 +
  10.214 +        System.out.println("Total checks made: " + checkCount);
  10.215 +    }
  10.216 +
  10.217 +    StaticArgumentsArity arity;
  10.218 +    StaticArgumentKind[] saks;
  10.219 +    JavaSource source;
  10.220 +    DiagChecker dc;
  10.221 +
  10.222 +    TestInvokeDynamic(StaticArgumentsArity arity, StaticArgumentKind... saks) {
  10.223 +        this.arity = arity;
  10.224 +        this.saks = saks;
  10.225 +        source = new JavaSource();
  10.226 +        dc = new DiagChecker();
  10.227 +    }
  10.228 +
  10.229 +    void compileAndCheck(JavaFileManager fm, JavaCompiler tool) throws Exception {
  10.230 +        JavacTaskImpl ct = (JavacTaskImpl)tool.getTask(null, fm, dc,
  10.231 +                null, null, Arrays.asList(source));
  10.232 +        Context context = ct.getContext();
  10.233 +        Symtab syms = Symtab.instance(context);
  10.234 +        Names names = Names.instance(context);
  10.235 +        ct.addTaskListener(new Indifier(syms, names));
  10.236 +        try {
  10.237 +            ct.generate();
  10.238 +        } catch (Throwable t) {
  10.239 +            t.printStackTrace();
  10.240 +            throw new AssertionError(String.format("Error thrown when compiling following code\n%s", source.source));
  10.241 +        }
  10.242 +        if (dc.diagFound) {
  10.243 +            throw new AssertionError(String.format("Diags found when compiling following code\n%s\n\n%s", source.source, dc.printDiags()));
  10.244 +        }
  10.245 +        verifyBytecode();
  10.246 +    }
  10.247 +
  10.248 +    void verifyBytecode() {
  10.249 +        File compiledTest = new File("Test.class");
  10.250 +        try {
  10.251 +            ClassFile cf = ClassFile.read(compiledTest);
  10.252 +            Method testMethod = null;
  10.253 +            for (Method m : cf.methods) {
  10.254 +                if (m.getName(cf.constant_pool).equals("test")) {
  10.255 +                    testMethod = m;
  10.256 +                    break;
  10.257 +                }
  10.258 +            }
  10.259 +            if (testMethod == null) {
  10.260 +                throw new Error("Test method not found");
  10.261 +            }
  10.262 +            Code_attribute ea = (Code_attribute)testMethod.attributes.get(Attribute.Code);
  10.263 +            if (testMethod == null) {
  10.264 +                throw new Error("Code attribute for test() method not found");
  10.265 +            }
  10.266 +
  10.267 +            int bsmIdx = -1;
  10.268 +
  10.269 +            for (Instruction i : ea.getInstructions()) {
  10.270 +                if (i.getMnemonic().equals("invokedynamic")) {
  10.271 +                    CONSTANT_InvokeDynamic_info indyInfo =
  10.272 +                            (CONSTANT_InvokeDynamic_info)cf.constant_pool.get(i.getShort(1));
  10.273 +                    bsmIdx = indyInfo.bootstrap_method_attr_index;
  10.274 +                    if (!indyInfo.getNameAndTypeInfo().getType().equals("()V")) {
  10.275 +                        throw new AssertionError("type mismatch for CONSTANT_InvokeDynamic_info");
  10.276 +                    }
  10.277 +                }
  10.278 +            }
  10.279 +            if (bsmIdx == -1) {
  10.280 +                throw new Error("Missing invokedynamic in generated code");
  10.281 +            }
  10.282 +
  10.283 +            BootstrapMethods_attribute bsm_attr = (BootstrapMethods_attribute)cf.getAttribute(Attribute.BootstrapMethods);
  10.284 +            if (bsm_attr.bootstrap_method_specifiers.length != 1) {
  10.285 +                throw new Error("Bad number of method specifiers in BootstrapMethods attribute");
  10.286 +            }
  10.287 +            BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec =
  10.288 +                    bsm_attr.bootstrap_method_specifiers[0];
  10.289 +
  10.290 +            if (bsm_spec.bootstrap_arguments.length != arity.arity) {
  10.291 +                throw new Error("Bad number of static invokedynamic args in BootstrapMethod attribute");
  10.292 +            }
  10.293 +
  10.294 +            int count = 0;
  10.295 +            for (StaticArgumentKind sak : saks) {
  10.296 +                if (!sak.check(cf.constant_pool.get(bsm_spec.bootstrap_arguments[count]))) {
  10.297 +                    throw new Error("Bad static argument value " + sak);
  10.298 +                }
  10.299 +                count++;
  10.300 +            }
  10.301 +
  10.302 +            CONSTANT_MethodHandle_info bsm_handle =
  10.303 +                    (CONSTANT_MethodHandle_info)cf.constant_pool.get(bsm_spec.bootstrap_method_ref);
  10.304 +
  10.305 +            if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) {
  10.306 +                throw new Error("Bad kind on boostrap method handle");
  10.307 +            }
  10.308 +
  10.309 +            CONSTANT_Methodref_info bsm_ref =
  10.310 +                    (CONSTANT_Methodref_info)cf.constant_pool.get(bsm_handle.reference_index);
  10.311 +
  10.312 +            if (!bsm_ref.getClassInfo().getName().equals("Bootstrap")) {
  10.313 +                throw new Error("Bad owner of boostrap method");
  10.314 +            }
  10.315 +
  10.316 +            if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) {
  10.317 +                throw new Error("Bad boostrap method name");
  10.318 +            }
  10.319 +
  10.320 +            if (!bsm_ref.getNameAndTypeInfo().getType().equals(asBSMSignatureString())) {
  10.321 +                throw new Error("Bad boostrap method type" + bsm_ref.getNameAndTypeInfo().getType() + " " + asBSMSignatureString());
  10.322 +            }
  10.323 +        } catch (Exception e) {
  10.324 +            e.printStackTrace();
  10.325 +            throw new Error("error reading " + compiledTest +": " + e);
  10.326 +        }
  10.327 +    }
  10.328 +
  10.329 +    String asBSMSignatureString() {
  10.330 +        StringBuilder buf = new StringBuilder();
  10.331 +        buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;");
  10.332 +        for (StaticArgumentKind sak : saks) {
  10.333 +            buf.append(sak.bytecodeTypeStr);
  10.334 +        }
  10.335 +        buf.append(")Ljava/lang/invoke/CallSite;");
  10.336 +        return buf.toString();
  10.337 +    }
  10.338 +
  10.339 +    class JavaSource extends SimpleJavaFileObject {
  10.340 +
  10.341 +        static final String source_template = "import java.lang.invoke.*;\n" +
  10.342 +                "class Bootstrap {\n" +
  10.343 +                "   public static CallSite bsm(MethodHandles.Lookup lookup, String name, MethodType methodType #SARGS) {\n" +
  10.344 +                "       return null;\n" +
  10.345 +                "   }\n" +
  10.346 +                "}\n" +
  10.347 +                "class Test {\n" +
  10.348 +                "   void m() { }\n" +
  10.349 +                "   void test() { m(); }\n" +
  10.350 +                "}";
  10.351 +
  10.352 +        String source;
  10.353 +
  10.354 +        JavaSource() {
  10.355 +            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
  10.356 +            source = source_template.replace("#SARGS", asSignatureString());
  10.357 +        }
  10.358 +
  10.359 +        @Override
  10.360 +        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
  10.361 +            return source;
  10.362 +        }
  10.363 +
  10.364 +        String asSignatureString() {
  10.365 +            int count = 0;
  10.366 +            StringBuilder buf = new StringBuilder();
  10.367 +            for (StaticArgumentKind sak : saks) {
  10.368 +                buf.append(",");
  10.369 +                buf.append(sak.sourceTypeStr);
  10.370 +                buf.append(' ');
  10.371 +                buf.append(String.format("x%d", count++));
  10.372 +            }
  10.373 +            return buf.toString();
  10.374 +        }
  10.375 +    }
  10.376 +
  10.377 +    class Indifier extends TreeScanner<Void, Void> implements TaskListener {
  10.378 +
  10.379 +        MethodSymbol bsm;
  10.380 +        Symtab syms;
  10.381 +        Names names;
  10.382 +
  10.383 +        Indifier(Symtab syms, Names names) {
  10.384 +            this.syms = syms;
  10.385 +            this.names = names;
  10.386 +        }
  10.387 +
  10.388 +        @Override
  10.389 +        public void started(TaskEvent e) {
  10.390 +            //do nothing
  10.391 +        }
  10.392 +
  10.393 +        @Override
  10.394 +        public void finished(TaskEvent e) {
  10.395 +            if (e.getKind() == TaskEvent.Kind.ANALYZE) {
  10.396 +                scan(e.getCompilationUnit(), null);
  10.397 +            }
  10.398 +        }
  10.399 +
  10.400 +        @Override
  10.401 +        public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
  10.402 +            super.visitMethodInvocation(node, p);
  10.403 +            JCMethodInvocation apply = (JCMethodInvocation)node;
  10.404 +            JCIdent ident = (JCIdent)apply.meth;
  10.405 +            Symbol oldSym = ident.sym;
  10.406 +            if (!oldSym.isConstructor()) {
  10.407 +                Object[] staticArgs = new Object[arity.arity];
  10.408 +                for (int i = 0; i < arity.arity ; i++) {
  10.409 +                    staticArgs[i] = saks[i].getValue(syms, names);
  10.410 +                }
  10.411 +                ident.sym = new Symbol.DynamicMethodSymbol(oldSym.name, oldSym.owner, REF_invokeStatic, bsm, oldSym.type, staticArgs);
  10.412 +            }
  10.413 +            return null;
  10.414 +        }
  10.415 +
  10.416 +        @Override
  10.417 +        public Void visitMethod(MethodTree node, Void p) {
  10.418 +            super.visitMethod(node, p);
  10.419 +            if (node.getName().toString().equals("bsm")) {
  10.420 +                bsm = ((JCMethodDecl)node).sym;
  10.421 +            }
  10.422 +            return null;
  10.423 +        }
  10.424 +    }
  10.425 +
  10.426 +    static class DiagChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
  10.427 +
  10.428 +        boolean diagFound;
  10.429 +        ArrayList<String> diags = new ArrayList<>();
  10.430 +
  10.431 +        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
  10.432 +            diags.add(diagnostic.getMessage(Locale.getDefault()));
  10.433 +            diagFound = true;
  10.434 +        }
  10.435 +
  10.436 +        String printDiags() {
  10.437 +            StringBuilder buf = new StringBuilder();
  10.438 +            for (String s : diags) {
  10.439 +                buf.append(s);
  10.440 +                buf.append("\n");
  10.441 +            }
  10.442 +            return buf.toString();
  10.443 +        }
  10.444 +    }
  10.445 +}

mercurial