test/tools/javac/lambda/TestInvokeDynamic.java

Fri, 30 May 2014 12:54:16 +0200

author
jlahoda
date
Fri, 30 May 2014 12:54:16 +0200
changeset 2410
e64bb2f5f0cf
parent 2186
6e0f31d61e56
child 2525
2eb010b6cb22
permissions
-rw-r--r--

8031967: For some sources compiler compiles for ever
Summary: Avoid creating DeferredTypes for method calls with method calls as receivers if the site can be determined reliably
Reviewed-by: mcimadamore, vromero
Contributed-by: maurizio.cimadamore@oracle.com, jan.lahoda@oracle.com

     1 /*
     2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     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  */
    24 /*
    25  * @test
    26  * @bug 7194586 8003280 8006694 8010404
    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 ../lib
    31  * @build JavacTestingAbstractThreadedTest
    32  * @run main/othervm TestInvokeDynamic
    33  */
    35 // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
    36 // see JDK-8006746
    38 import java.io.File;
    39 import java.net.URI;
    40 import java.util.ArrayList;
    41 import java.util.Arrays;
    42 import java.util.Locale;
    44 import javax.tools.Diagnostic;
    45 import javax.tools.JavaFileObject;
    46 import javax.tools.SimpleJavaFileObject;
    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;
    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;
    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;
    75 import static com.sun.tools.javac.jvm.ClassFile.*;
    77 public class TestInvokeDynamic
    78     extends JavacTestingAbstractThreadedTest
    79     implements Runnable {
    81     enum StaticArgumentKind {
    82         STRING("Hello!", "String", "Ljava/lang/String;") {
    83             @Override
    84             boolean check(CPInfo cpInfo) throws Exception {
    85                 return (cpInfo instanceof CONSTANT_String_info) &&
    86                         ((CONSTANT_String_info)cpInfo).getString()
    87                         .equals(value);
    88             }
    89         },
    90         CLASS(null, "Class<?>", "Ljava/lang/Class;") {
    91             @Override
    92             boolean check(CPInfo cpInfo) throws Exception {
    93                 return (cpInfo instanceof CONSTANT_Class_info) &&
    94                         ((CONSTANT_Class_info)cpInfo).getName()
    95                         .equals("java/lang/String");
    96             }
    97         },
    98         INTEGER(1, "int", "I") {
    99             @Override
   100             boolean check(CPInfo cpInfo) throws Exception {
   101                 return (cpInfo instanceof CONSTANT_Integer_info) &&
   102                         ((CONSTANT_Integer_info)cpInfo).value ==
   103                         ((Integer)value).intValue();
   104             }
   105         },
   106         LONG(1L, "long", "J") {
   107             @Override
   108             boolean check(CPInfo cpInfo) throws Exception {
   109                 return (cpInfo instanceof CONSTANT_Long_info) &&
   110                         ((CONSTANT_Long_info)cpInfo).value ==
   111                         ((Long)value).longValue();
   112             }
   113         },
   114         FLOAT(1.0f, "float", "F") {
   115             @Override
   116             boolean check(CPInfo cpInfo) throws Exception {
   117                 return (cpInfo instanceof CONSTANT_Float_info) &&
   118                         ((CONSTANT_Float_info)cpInfo).value ==
   119                         ((Float)value).floatValue();
   120             }
   121         },
   122         DOUBLE(1.0, "double","D") {
   123             @Override
   124             boolean check(CPInfo cpInfo) throws Exception {
   125                 return (cpInfo instanceof CONSTANT_Double_info) &&
   126                         ((CONSTANT_Double_info)cpInfo).value ==
   127                         ((Double)value).doubleValue();
   128             }
   129         },
   130         METHOD_HANDLE(null, "MethodHandle", "Ljava/lang/invoke/MethodHandle;") {
   131             @Override
   132             boolean check(CPInfo cpInfo) throws Exception {
   133                 if (!(cpInfo instanceof CONSTANT_MethodHandle_info))
   134                     return false;
   135                 CONSTANT_MethodHandle_info handleInfo =
   136                         (CONSTANT_MethodHandle_info)cpInfo;
   137                 return handleInfo.getCPRefInfo().getClassName().equals("Array") &&
   138                         handleInfo.reference_kind == RefKind.REF_invokeVirtual &&
   139                         handleInfo.getCPRefInfo()
   140                         .getNameAndTypeInfo().getName().equals("clone") &&
   141                         handleInfo.getCPRefInfo()
   142                         .getNameAndTypeInfo().getType().equals("()Ljava/lang/Object;");
   143             }
   144         },
   145         METHOD_TYPE(null, "MethodType", "Ljava/lang/invoke/MethodType;") {
   146             @Override
   147             boolean check(CPInfo cpInfo) throws Exception {
   148                 return (cpInfo instanceof CONSTANT_MethodType_info) &&
   149                         ((CONSTANT_MethodType_info)cpInfo).getType()
   150                         .equals("()Ljava/lang/Object;");
   151             }
   152         };
   154         Object value;
   155         String sourceTypeStr;
   156         String bytecodeTypeStr;
   158         StaticArgumentKind(Object value, String sourceTypeStr,
   159                 String bytecodeTypeStr) {
   160             this.value = value;
   161             this.sourceTypeStr = sourceTypeStr;
   162             this.bytecodeTypeStr = bytecodeTypeStr;
   163         }
   165         abstract boolean check(CPInfo cpInfo) throws Exception;
   167         Object getValue(Symtab syms, Names names, Types types) {
   168             switch (this) {
   169                 case STRING:
   170                 case INTEGER:
   171                 case LONG:
   172                 case FLOAT:
   173                 case DOUBLE:
   174                     return value;
   175                 case CLASS:
   176                     return syms.stringType.tsym;
   177                 case METHOD_HANDLE:
   178                     return new Pool.MethodHandle(REF_invokeVirtual,
   179                             syms.arrayCloneMethod, types);
   180                 case METHOD_TYPE:
   181                     return syms.arrayCloneMethod.type;
   182                 default:
   183                     throw new AssertionError();
   184             }
   185         }
   186     }
   188     enum StaticArgumentsArity {
   189         ZERO(0),
   190         ONE(1),
   191         TWO(2),
   192         THREE(3);
   194         int arity;
   196         StaticArgumentsArity(int arity) {
   197             this.arity = arity;
   198         }
   199     }
   201     public static void main(String... args) throws Exception {
   202         for (StaticArgumentsArity arity : StaticArgumentsArity.values()) {
   203             if (arity.arity == 0) {
   204                 pool.execute(new TestInvokeDynamic(arity));
   205             } else {
   206                 for (StaticArgumentKind sak1 : StaticArgumentKind.values()) {
   207                     if (arity.arity == 1) {
   208                         pool.execute(new TestInvokeDynamic(arity, sak1));
   209                     } else {
   210                         for (StaticArgumentKind sak2 : StaticArgumentKind.values()) {
   211                             if (arity.arity == 2) {
   212                                 pool.execute(new TestInvokeDynamic(arity, sak1, sak2));
   213                             } else {
   214                                 for (StaticArgumentKind sak3 : StaticArgumentKind.values()) {
   215                                     pool.execute(
   216                                         new TestInvokeDynamic(arity, sak1, sak2, sak3));
   217                                 }
   218                             }
   219                         }
   220                     }
   221                 }
   222             }
   223         }
   225         checkAfterExec();
   226     }
   228     StaticArgumentsArity arity;
   229     StaticArgumentKind[] saks;
   230     DiagChecker dc;
   232     TestInvokeDynamic(StaticArgumentsArity arity, StaticArgumentKind... saks) {
   233         this.arity = arity;
   234         this.saks = saks;
   235         dc = new DiagChecker();
   236     }
   238     public void run() {
   239         int id = checkCount.incrementAndGet();
   240         JavaSource source = new JavaSource(id);
   241         JavacTaskImpl ct = (JavacTaskImpl)comp.getTask(null, fm.get(), dc,
   242                 Arrays.asList("-g"), null, Arrays.asList(source));
   243         Context context = ct.getContext();
   244         Symtab syms = Symtab.instance(context);
   245         Names names = Names.instance(context);
   246         Types types = Types.instance(context);
   247         ct.addTaskListener(new Indifier(syms, names, types));
   248         try {
   249             ct.generate();
   250         } catch (Throwable t) {
   251             t.printStackTrace();
   252             throw new AssertionError(
   253                     String.format("Error thrown when compiling following code\n%s",
   254                     source.source));
   255         }
   256         if (dc.diagFound) {
   257             throw new AssertionError(
   258                     String.format("Diags found when compiling following code\n%s\n\n%s",
   259                     source.source, dc.printDiags()));
   260         }
   261         verifyBytecode(id);
   262     }
   264     void verifyBytecode(int id) {
   265         File compiledTest = new File(String.format("Test%d.class", id));
   266         try {
   267             ClassFile cf = ClassFile.read(compiledTest);
   268             Method testMethod = null;
   269             for (Method m : cf.methods) {
   270                 if (m.getName(cf.constant_pool).equals("test")) {
   271                     testMethod = m;
   272                     break;
   273                 }
   274             }
   275             if (testMethod == null) {
   276                 throw new Error("Test method not found");
   277             }
   278             Code_attribute ea =
   279                     (Code_attribute)testMethod.attributes.get(Attribute.Code);
   280             if (testMethod == null) {
   281                 throw new Error("Code attribute for test() method not found");
   282             }
   284             int bsmIdx = -1;
   286             for (Instruction i : ea.getInstructions()) {
   287                 if (i.getMnemonic().equals("invokedynamic")) {
   288                     CONSTANT_InvokeDynamic_info indyInfo =
   289                          (CONSTANT_InvokeDynamic_info)cf
   290                             .constant_pool.get(i.getShort(1));
   291                     bsmIdx = indyInfo.bootstrap_method_attr_index;
   292                     if (!indyInfo.getNameAndTypeInfo().getType().equals("()V")) {
   293                         throw new
   294                             AssertionError("type mismatch for CONSTANT_InvokeDynamic_info");
   295                     }
   296                 }
   297             }
   298             if (bsmIdx == -1) {
   299                 throw new Error("Missing invokedynamic in generated code");
   300             }
   302             BootstrapMethods_attribute bsm_attr =
   303                     (BootstrapMethods_attribute)cf
   304                     .getAttribute(Attribute.BootstrapMethods);
   305             if (bsm_attr.bootstrap_method_specifiers.length != 1) {
   306                 throw new Error("Bad number of method specifiers " +
   307                         "in BootstrapMethods attribute");
   308             }
   309             BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec =
   310                     bsm_attr.bootstrap_method_specifiers[0];
   312             if (bsm_spec.bootstrap_arguments.length != arity.arity) {
   313                 throw new Error("Bad number of static invokedynamic args " +
   314                         "in BootstrapMethod attribute");
   315             }
   317             int count = 0;
   318             for (StaticArgumentKind sak : saks) {
   319                 if (!sak.check(cf.constant_pool
   320                         .get(bsm_spec.bootstrap_arguments[count]))) {
   321                     throw new Error("Bad static argument value " + sak);
   322                 }
   323                 count++;
   324             }
   326             CONSTANT_MethodHandle_info bsm_handle =
   327                     (CONSTANT_MethodHandle_info)cf.constant_pool
   328                     .get(bsm_spec.bootstrap_method_ref);
   330             if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) {
   331                 throw new Error("Bad kind on boostrap method handle");
   332             }
   334             CONSTANT_Methodref_info bsm_ref =
   335                     (CONSTANT_Methodref_info)cf.constant_pool
   336                     .get(bsm_handle.reference_index);
   338             if (!bsm_ref.getClassInfo().getName().equals("Bootstrap")) {
   339                 throw new Error("Bad owner of boostrap method");
   340             }
   342             if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) {
   343                 throw new Error("Bad boostrap method name");
   344             }
   346             if (!bsm_ref.getNameAndTypeInfo()
   347                     .getType().equals(asBSMSignatureString())) {
   348                 throw new Error("Bad boostrap method type" +
   349                         bsm_ref.getNameAndTypeInfo().getType() + " " +
   350                         asBSMSignatureString());
   351             }
   353             LineNumberTable_attribute lnt =
   354                     (LineNumberTable_attribute)ea.attributes.get(Attribute.LineNumberTable);
   356             if (lnt == null) {
   357                 throw new Error("No LineNumberTable attribute");
   358             }
   359             if (lnt.line_number_table_length != 3) {
   360                 throw new Error("Wrong number of entries in LineNumberTable");
   361             }
   362         } catch (Exception e) {
   363             e.printStackTrace();
   364             throw new Error("error reading " + compiledTest +": " + e);
   365         }
   366     }
   368     String asBSMSignatureString() {
   369         StringBuilder buf = new StringBuilder();
   370         buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;");
   371         for (StaticArgumentKind sak : saks) {
   372             buf.append(sak.bytecodeTypeStr);
   373         }
   374         buf.append(")Ljava/lang/invoke/CallSite;");
   375         return buf.toString();
   376     }
   378     class JavaSource extends SimpleJavaFileObject {
   380         static final String source_template = "import java.lang.invoke.*;\n" +
   381                 "class Bootstrap {\n" +
   382                 "   public static CallSite bsm(MethodHandles.Lookup lookup, " +
   383                 "String name, MethodType methodType #SARGS) {\n" +
   384                 "       return null;\n" +
   385                 "   }\n" +
   386                 "}\n" +
   387                 "class Test#ID {\n" +
   388                 "   void m() { }\n" +
   389                 "   void test() {\n" +
   390                 "      Object o = this; // marker statement \n" +
   391                 "      m();\n" +
   392                 "   }\n" +
   393                 "}";
   395         String source;
   397         JavaSource(int id) {
   398             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
   399             source = source_template.replace("#SARGS", asSignatureString())
   400                     .replace("#ID", String.valueOf(id));
   401         }
   403         @Override
   404         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
   405             return source;
   406         }
   408         String asSignatureString() {
   409             int count = 0;
   410             StringBuilder buf = new StringBuilder();
   411             for (StaticArgumentKind sak : saks) {
   412                 buf.append(",");
   413                 buf.append(sak.sourceTypeStr);
   414                 buf.append(' ');
   415                 buf.append(String.format("x%d", count++));
   416             }
   417             return buf.toString();
   418         }
   419     }
   421     class Indifier extends TreeScanner<Void, Void> implements TaskListener {
   423         MethodSymbol bsm;
   424         Symtab syms;
   425         Names names;
   426         Types types;
   428         Indifier(Symtab syms, Names names, Types types) {
   429             this.syms = syms;
   430             this.names = names;
   431             this.types = types;
   432         }
   434         @Override
   435         public void started(TaskEvent e) {
   436             //do nothing
   437         }
   439         @Override
   440         public void finished(TaskEvent e) {
   441             if (e.getKind() == TaskEvent.Kind.ANALYZE) {
   442                 scan(e.getCompilationUnit(), null);
   443             }
   444         }
   446         @Override
   447         public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
   448             super.visitMethodInvocation(node, p);
   449             JCMethodInvocation apply = (JCMethodInvocation)node;
   450             JCIdent ident = (JCIdent)apply.meth;
   451             Symbol oldSym = ident.sym;
   452             if (!oldSym.isConstructor()) {
   453                 Object[] staticArgs = new Object[arity.arity];
   454                 for (int i = 0; i < arity.arity ; i++) {
   455                     staticArgs[i] = saks[i].getValue(syms, names, types);
   456                 }
   457                 ident.sym = new Symbol.DynamicMethodSymbol(oldSym.name,
   458                         oldSym.owner, REF_invokeStatic, bsm, oldSym.type, staticArgs);
   459             }
   460             return null;
   461         }
   463         @Override
   464         public Void visitMethod(MethodTree node, Void p) {
   465             super.visitMethod(node, p);
   466             if (node.getName().toString().equals("bsm")) {
   467                 bsm = ((JCMethodDecl)node).sym;
   468             }
   469             return null;
   470         }
   471     }
   473     static class DiagChecker
   474         implements javax.tools.DiagnosticListener<JavaFileObject> {
   476         boolean diagFound;
   477         ArrayList<String> diags = new ArrayList<>();
   479         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   480             diags.add(diagnostic.getMessage(Locale.getDefault()));
   481             diagFound = true;
   482         }
   484         String printDiags() {
   485             StringBuilder buf = new StringBuilder();
   486             for (String s : diags) {
   487                 buf.append(s);
   488                 buf.append("\n");
   489             }
   490             return buf.toString();
   491         }
   492     }
   494 }

mercurial