test/langtools/tools/javac/lambda/TestInvokeDynamic.java
changeset 47216 71c04702a3d5
parent 32454 b0ac04e0fefe
child 47350 d65c3b21081c
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2012, 2015, 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.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 /*
       
    25  * @test
       
    26  * @bug 7194586 8003280 8006694 8010404 8129962
       
    27  * @summary Add lambda tests
       
    28  *  Add back-end support for invokedynamic
       
    29  *  temporarily workaround combo tests are causing time out in several platforms
       
    30  * @library /tools/javac/lib
       
    31  * @modules jdk.jdeps/com.sun.tools.classfile
       
    32  *          jdk.compiler/com.sun.tools.javac.api
       
    33  *          jdk.compiler/com.sun.tools.javac.code
       
    34  *          jdk.compiler/com.sun.tools.javac.comp
       
    35  *          jdk.compiler/com.sun.tools.javac.main
       
    36  *          jdk.compiler/com.sun.tools.javac.jvm
       
    37  *          jdk.compiler/com.sun.tools.javac.tree
       
    38  *          jdk.compiler/com.sun.tools.javac.util
       
    39  * @build combo.ComboTestHelper
       
    40  * @run main TestInvokeDynamic
       
    41  */
       
    42 
       
    43 import java.io.IOException;
       
    44 import java.io.InputStream;
       
    45 
       
    46 import javax.tools.JavaFileObject;
       
    47 
       
    48 import com.sun.source.tree.MethodInvocationTree;
       
    49 import com.sun.source.tree.MethodTree;
       
    50 import com.sun.source.util.TaskEvent;
       
    51 import com.sun.source.util.TaskListener;
       
    52 import com.sun.source.util.TreeScanner;
       
    53 
       
    54 import com.sun.tools.classfile.Attribute;
       
    55 import com.sun.tools.classfile.BootstrapMethods_attribute;
       
    56 import com.sun.tools.classfile.ClassFile;
       
    57 import com.sun.tools.classfile.Code_attribute;
       
    58 import com.sun.tools.classfile.ConstantPool.*;
       
    59 import com.sun.tools.classfile.Instruction;
       
    60 import com.sun.tools.classfile.LineNumberTable_attribute;
       
    61 import com.sun.tools.classfile.Method;
       
    62 
       
    63 import com.sun.tools.javac.api.JavacTaskImpl;
       
    64 import com.sun.tools.javac.code.Symbol;
       
    65 import com.sun.tools.javac.code.Symbol.MethodSymbol;
       
    66 import com.sun.tools.javac.code.Symtab;
       
    67 import com.sun.tools.javac.code.Types;
       
    68 import com.sun.tools.javac.jvm.Pool;
       
    69 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
       
    70 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
       
    71 import com.sun.tools.javac.tree.JCTree.JCIdent;
       
    72 import com.sun.tools.javac.util.Context;
       
    73 import com.sun.tools.javac.util.Names;
       
    74 
       
    75 import combo.ComboParameter;
       
    76 import combo.ComboTask;
       
    77 import combo.ComboTestHelper;
       
    78 import combo.ComboInstance;
       
    79 import combo.ComboTask.Result;
       
    80 
       
    81 import static com.sun.tools.javac.jvm.ClassFile.*;
       
    82 
       
    83 public class TestInvokeDynamic extends ComboInstance<TestInvokeDynamic> {
       
    84 
       
    85     enum StaticArgumentKind implements ComboParameter {
       
    86         STRING("Hello!", "String", "Ljava/lang/String;") {
       
    87             @Override
       
    88             boolean check(CPInfo cpInfo) throws Exception {
       
    89                 return (cpInfo instanceof CONSTANT_String_info) &&
       
    90                         ((CONSTANT_String_info)cpInfo).getString()
       
    91                         .equals(value);
       
    92             }
       
    93         },
       
    94         CLASS(null, "Class<?>", "Ljava/lang/Class;") {
       
    95             @Override
       
    96             boolean check(CPInfo cpInfo) throws Exception {
       
    97                 return (cpInfo instanceof CONSTANT_Class_info) &&
       
    98                         ((CONSTANT_Class_info)cpInfo).getName()
       
    99                         .equals("java/lang/String");
       
   100             }
       
   101         },
       
   102         INTEGER(1, "int", "I") {
       
   103             @Override
       
   104             boolean check(CPInfo cpInfo) throws Exception {
       
   105                 return (cpInfo instanceof CONSTANT_Integer_info) &&
       
   106                         ((CONSTANT_Integer_info)cpInfo).value ==
       
   107                         ((Integer)value).intValue();
       
   108             }
       
   109         },
       
   110         LONG(1L, "long", "J") {
       
   111             @Override
       
   112             boolean check(CPInfo cpInfo) throws Exception {
       
   113                 return (cpInfo instanceof CONSTANT_Long_info) &&
       
   114                         ((CONSTANT_Long_info)cpInfo).value ==
       
   115                         ((Long)value).longValue();
       
   116             }
       
   117         },
       
   118         FLOAT(1.0f, "float", "F") {
       
   119             @Override
       
   120             boolean check(CPInfo cpInfo) throws Exception {
       
   121                 return (cpInfo instanceof CONSTANT_Float_info) &&
       
   122                         ((CONSTANT_Float_info)cpInfo).value ==
       
   123                         ((Float)value).floatValue();
       
   124             }
       
   125         },
       
   126         DOUBLE(1.0, "double","D") {
       
   127             @Override
       
   128             boolean check(CPInfo cpInfo) throws Exception {
       
   129                 return (cpInfo instanceof CONSTANT_Double_info) &&
       
   130                         ((CONSTANT_Double_info)cpInfo).value ==
       
   131                         ((Double)value).doubleValue();
       
   132             }
       
   133         },
       
   134         METHOD_HANDLE(null, "MethodHandle", "Ljava/lang/invoke/MethodHandle;") {
       
   135             @Override
       
   136             boolean check(CPInfo cpInfo) throws Exception {
       
   137                 if (!(cpInfo instanceof CONSTANT_MethodHandle_info))
       
   138                     return false;
       
   139                 CONSTANT_MethodHandle_info handleInfo =
       
   140                         (CONSTANT_MethodHandle_info)cpInfo;
       
   141                 return handleInfo.getCPRefInfo().getClassName().equals("Array") &&
       
   142                         handleInfo.reference_kind == RefKind.REF_invokeVirtual &&
       
   143                         handleInfo.getCPRefInfo()
       
   144                         .getNameAndTypeInfo().getName().equals("clone") &&
       
   145                         handleInfo.getCPRefInfo()
       
   146                         .getNameAndTypeInfo().getType().equals("()Ljava/lang/Object;");
       
   147             }
       
   148         },
       
   149         METHOD_TYPE(null, "MethodType", "Ljava/lang/invoke/MethodType;") {
       
   150             @Override
       
   151             boolean check(CPInfo cpInfo) throws Exception {
       
   152                 return (cpInfo instanceof CONSTANT_MethodType_info) &&
       
   153                         ((CONSTANT_MethodType_info)cpInfo).getType()
       
   154                         .equals("()Ljava/lang/Object;");
       
   155             }
       
   156         };
       
   157 
       
   158         Object value;
       
   159         String sourceTypeStr;
       
   160         String bytecodeTypeStr;
       
   161 
       
   162         StaticArgumentKind(Object value, String sourceTypeStr,
       
   163                 String bytecodeTypeStr) {
       
   164             this.value = value;
       
   165             this.sourceTypeStr = sourceTypeStr;
       
   166             this.bytecodeTypeStr = bytecodeTypeStr;
       
   167         }
       
   168 
       
   169         abstract boolean check(CPInfo cpInfo) throws Exception;
       
   170 
       
   171         Object getValue(Symtab syms, Names names, Types types) {
       
   172             switch (this) {
       
   173                 case STRING:
       
   174                 case INTEGER:
       
   175                 case LONG:
       
   176                 case FLOAT:
       
   177                 case DOUBLE:
       
   178                     return value;
       
   179                 case CLASS:
       
   180                     return syms.stringType.tsym;
       
   181                 case METHOD_HANDLE:
       
   182                     return new Pool.MethodHandle(REF_invokeVirtual,
       
   183                             syms.arrayCloneMethod, types);
       
   184                 case METHOD_TYPE:
       
   185                     return syms.arrayCloneMethod.type;
       
   186                 default:
       
   187                     throw new AssertionError();
       
   188             }
       
   189         }
       
   190 
       
   191         @Override
       
   192         public String expand(String optParameter) {
       
   193             return sourceTypeStr;
       
   194         }
       
   195     }
       
   196 
       
   197     enum StaticArgumentsArity implements ComboParameter {
       
   198         ZERO(0, ""),
       
   199         ONE(1, ",#{SARG[0]} s1"),
       
   200         TWO(2, ",#{SARG[0]} s1, #{SARG[1]} s2"),
       
   201         THREE(3, ",#{SARG[0]} s1, #{SARG[1]} s2, #{SARG[2]} s3");
       
   202 
       
   203         int arity;
       
   204         String argsTemplate;
       
   205 
       
   206         StaticArgumentsArity(int arity, String argsTemplate) {
       
   207             this.arity = arity;
       
   208             this.argsTemplate = argsTemplate;
       
   209         }
       
   210 
       
   211         @Override
       
   212         public String expand(String optParameter) {
       
   213             return argsTemplate;
       
   214         }
       
   215     }
       
   216 
       
   217     public static void main(String... args) throws Exception {
       
   218         new ComboTestHelper<TestInvokeDynamic>()
       
   219                 .withFilter(TestInvokeDynamic::redundantTestFilter)
       
   220                 .withDimension("SARGS", (x, arity) -> x.arity = arity, StaticArgumentsArity.values())
       
   221                 .withArrayDimension("SARG", (x, arg, idx) -> x.saks[idx] = arg, 3, StaticArgumentKind.values())
       
   222                 .run(TestInvokeDynamic::new);
       
   223     }
       
   224 
       
   225     StaticArgumentsArity arity;
       
   226     StaticArgumentKind[] saks = new StaticArgumentKind[3];
       
   227 
       
   228     boolean redundantTestFilter() {
       
   229         for (int i = arity.arity ; i < saks.length ; i++) {
       
   230             if (saks[i].ordinal() != 0) {
       
   231                 return false;
       
   232             }
       
   233         }
       
   234         return true;
       
   235     }
       
   236 
       
   237     final String source_template =
       
   238                 "import java.lang.invoke.*;\n" +
       
   239                 "class Test {\n" +
       
   240                 "   void m() { }\n" +
       
   241                 "   void test() {\n" +
       
   242                 "      Object o = this; // marker statement \n" +
       
   243                 "      m();\n" +
       
   244                 "   }\n" +
       
   245                 "}\n" +
       
   246                 "class Bootstrap {\n" +
       
   247                 "   public static CallSite bsm(MethodHandles.Lookup lookup, " +
       
   248                 "String name, MethodType methodType #{SARGS}) {\n" +
       
   249                 "       return null;\n" +
       
   250                 "   }\n" +
       
   251                 "}";
       
   252 
       
   253     @Override
       
   254     public void doWork() throws IOException {
       
   255         ComboTask comboTask = newCompilationTask()
       
   256                 .withOption("-g")
       
   257                 .withSourceFromTemplate(source_template);
       
   258 
       
   259         JavacTaskImpl ct = (JavacTaskImpl)comboTask.getTask();
       
   260         Context context = ct.getContext();
       
   261         Symtab syms = Symtab.instance(context);
       
   262         Names names = Names.instance(context);
       
   263         Types types = Types.instance(context);
       
   264         ct.addTaskListener(new Indifier(syms, names, types));
       
   265         verifyBytecode(comboTask.generate());
       
   266     }
       
   267 
       
   268     void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) {
       
   269         if (res.hasErrors()) {
       
   270             fail("Diags found when compiling instance: " + res.compilationInfo());
       
   271             return;
       
   272         }
       
   273         try (InputStream is = res.get().iterator().next().openInputStream()){
       
   274             ClassFile cf = ClassFile.read(is);
       
   275             Method testMethod = null;
       
   276             for (Method m : cf.methods) {
       
   277                 if (m.getName(cf.constant_pool).equals("test")) {
       
   278                     testMethod = m;
       
   279                     break;
       
   280                 }
       
   281             }
       
   282             if (testMethod == null) {
       
   283                 fail("Test method not found");
       
   284                 return;
       
   285             }
       
   286             Code_attribute ea =
       
   287                     (Code_attribute)testMethod.attributes.get(Attribute.Code);
       
   288             if (testMethod == null) {
       
   289                 fail("Code attribute for test() method not found");
       
   290                 return;
       
   291             }
       
   292 
       
   293             int bsmIdx = -1;
       
   294 
       
   295             for (Instruction i : ea.getInstructions()) {
       
   296                 if (i.getMnemonic().equals("invokedynamic")) {
       
   297                     CONSTANT_InvokeDynamic_info indyInfo =
       
   298                          (CONSTANT_InvokeDynamic_info)cf
       
   299                             .constant_pool.get(i.getShort(1));
       
   300                     bsmIdx = indyInfo.bootstrap_method_attr_index;
       
   301                     if (!indyInfo.getNameAndTypeInfo().getType().equals("()V")) {
       
   302                         fail("type mismatch for CONSTANT_InvokeDynamic_info");
       
   303                         return;
       
   304                     }
       
   305                 }
       
   306             }
       
   307             if (bsmIdx == -1) {
       
   308                 fail("Missing invokedynamic in generated code");
       
   309                 return;
       
   310             }
       
   311 
       
   312             BootstrapMethods_attribute bsm_attr =
       
   313                     (BootstrapMethods_attribute)cf
       
   314                     .getAttribute(Attribute.BootstrapMethods);
       
   315             if (bsm_attr.bootstrap_method_specifiers.length != 1) {
       
   316                 fail("Bad number of method specifiers " +
       
   317                         "in BootstrapMethods attribute");
       
   318                 return;
       
   319             }
       
   320             BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec =
       
   321                     bsm_attr.bootstrap_method_specifiers[0];
       
   322 
       
   323             if (bsm_spec.bootstrap_arguments.length != arity.arity) {
       
   324                 fail("Bad number of static invokedynamic args " +
       
   325                         "in BootstrapMethod attribute");
       
   326                 return;
       
   327             }
       
   328 
       
   329             for (int i = 0 ; i < arity.arity ; i++) {
       
   330                 if (!saks[i].check(cf.constant_pool
       
   331                         .get(bsm_spec.bootstrap_arguments[i]))) {
       
   332                     fail("Bad static argument value " + saks[i]);
       
   333                     return;
       
   334                 }
       
   335             }
       
   336 
       
   337             CONSTANT_MethodHandle_info bsm_handle =
       
   338                     (CONSTANT_MethodHandle_info)cf.constant_pool
       
   339                     .get(bsm_spec.bootstrap_method_ref);
       
   340 
       
   341             if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) {
       
   342                 fail("Bad kind on boostrap method handle");
       
   343                 return;
       
   344             }
       
   345 
       
   346             CONSTANT_Methodref_info bsm_ref =
       
   347                     (CONSTANT_Methodref_info)cf.constant_pool
       
   348                     .get(bsm_handle.reference_index);
       
   349 
       
   350             if (!bsm_ref.getClassInfo().getName().equals("Bootstrap")) {
       
   351                 fail("Bad owner of boostrap method");
       
   352                 return;
       
   353             }
       
   354 
       
   355             if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) {
       
   356                 fail("Bad boostrap method name");
       
   357                 return;
       
   358             }
       
   359 
       
   360             if (!bsm_ref.getNameAndTypeInfo()
       
   361                     .getType().equals(asBSMSignatureString())) {
       
   362                 fail("Bad boostrap method type" +
       
   363                         bsm_ref.getNameAndTypeInfo().getType() + " " +
       
   364                         asBSMSignatureString());
       
   365                 return;
       
   366             }
       
   367 
       
   368             LineNumberTable_attribute lnt =
       
   369                     (LineNumberTable_attribute)ea.attributes.get(Attribute.LineNumberTable);
       
   370 
       
   371             if (lnt == null) {
       
   372                 fail("No LineNumberTable attribute");
       
   373                 return;
       
   374             }
       
   375             if (lnt.line_number_table_length != 3) {
       
   376                 fail("Wrong number of entries in LineNumberTable");
       
   377                 return;
       
   378             }
       
   379         } catch (Exception e) {
       
   380             e.printStackTrace();
       
   381             fail("error reading classfile: " + res.compilationInfo());
       
   382             return;
       
   383         }
       
   384     }
       
   385 
       
   386     String asBSMSignatureString() {
       
   387         StringBuilder buf = new StringBuilder();
       
   388         buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;");
       
   389         for (int i = 0 ; i < arity.arity ; i++) {
       
   390             buf.append(saks[i].bytecodeTypeStr);
       
   391         }
       
   392         buf.append(")Ljava/lang/invoke/CallSite;");
       
   393         return buf.toString();
       
   394     }
       
   395 
       
   396     class Indifier extends TreeScanner<Void, Void> implements TaskListener {
       
   397 
       
   398         MethodSymbol bsm;
       
   399         Symtab syms;
       
   400         Names names;
       
   401         Types types;
       
   402 
       
   403         Indifier(Symtab syms, Names names, Types types) {
       
   404             this.syms = syms;
       
   405             this.names = names;
       
   406             this.types = types;
       
   407         }
       
   408 
       
   409         @Override
       
   410         public void started(TaskEvent e) {
       
   411             //do nothing
       
   412         }
       
   413 
       
   414         @Override
       
   415         public void finished(TaskEvent e) {
       
   416             if (e.getKind() == TaskEvent.Kind.ANALYZE) {
       
   417                 scan(e.getCompilationUnit(), null);
       
   418             }
       
   419         }
       
   420 
       
   421         @Override
       
   422         public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
       
   423             super.visitMethodInvocation(node, p);
       
   424             JCMethodInvocation apply = (JCMethodInvocation)node;
       
   425             JCIdent ident = (JCIdent)apply.meth;
       
   426             Symbol oldSym = ident.sym;
       
   427             if (!oldSym.isConstructor()) {
       
   428                 Object[] staticArgs = new Object[arity.arity];
       
   429                 for (int i = 0; i < arity.arity ; i++) {
       
   430                     staticArgs[i] = saks[i].getValue(syms, names, types);
       
   431                 }
       
   432                 ident.sym = new Symbol.DynamicMethodSymbol(oldSym.name,
       
   433                         oldSym.owner, REF_invokeStatic, bsm, oldSym.type, staticArgs);
       
   434             }
       
   435             return null;
       
   436         }
       
   437 
       
   438         @Override
       
   439         public Void visitMethod(MethodTree node, Void p) {
       
   440             super.visitMethod(node, p);
       
   441             if (node.getName().toString().equals("bsm")) {
       
   442                 bsm = ((JCMethodDecl)node).sym;
       
   443             }
       
   444             return null;
       
   445         }
       
   446     }
       
   447 }