test/tools/javac/lambda/TestInvokeDynamic.java

Mon, 08 Apr 2013 15:53:08 +0100

author
mcimadamore
date
Mon, 08 Apr 2013 15:53:08 +0100
changeset 1676
e9d986381414
parent 1520
5c956be64b9e
child 1774
37295244f534
permissions
-rw-r--r--

8010404: Lambda debugging: redundant LineNumberTable entry for lambda capture
Summary: Ignore indy entries in LineNumberTable
Reviewed-by: jjg

     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
    27  * @bug 8003280 8006694 8010404
    28  * @summary Add lambda tests
    29  *  Add back-end support for invokedynamic
    30  *  temporarily workaround combo tests are causing time out in several platforms
    31  * @library ../lib
    32  * @build JavacTestingAbstractThreadedTest
    33  * @run main/othervm TestInvokeDynamic
    34  */
    36 // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
    37 // see JDK-8006746
    39 import com.sun.source.tree.MethodInvocationTree;
    40 import com.sun.source.tree.MethodTree;
    41 import com.sun.source.util.TaskEvent;
    42 import com.sun.source.util.TaskListener;
    43 import com.sun.source.util.TreeScanner;
    45 import com.sun.tools.classfile.Attribute;
    46 import com.sun.tools.classfile.BootstrapMethods_attribute;
    47 import com.sun.tools.classfile.ClassFile;
    48 import com.sun.tools.classfile.Code_attribute;
    49 import com.sun.tools.classfile.ConstantPool.*;
    50 import com.sun.tools.classfile.Instruction;
    51 import com.sun.tools.classfile.LineNumberTable_attribute;
    52 import com.sun.tools.classfile.Method;
    54 import com.sun.tools.javac.api.JavacTaskImpl;
    55 import com.sun.tools.javac.code.Symbol;
    56 import com.sun.tools.javac.code.Symbol.MethodSymbol;
    57 import com.sun.tools.javac.code.Symtab;
    58 import com.sun.tools.javac.code.Types;
    59 import com.sun.tools.javac.jvm.Pool;
    60 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
    61 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
    62 import com.sun.tools.javac.tree.JCTree.JCIdent;
    63 import com.sun.tools.javac.util.Context;
    64 import com.sun.tools.javac.util.Names;
    66 import java.io.File;
    67 import java.net.URI;
    68 import java.util.ArrayList;
    69 import java.util.Arrays;
    70 import java.util.Locale;
    72 import javax.tools.Diagnostic;
    73 import javax.tools.JavaFileObject;
    74 import javax.tools.SimpleJavaFileObject;
    76 import static com.sun.tools.javac.jvm.ClassFile.*;
    78 public class TestInvokeDynamic
    79     extends JavacTestingAbstractThreadedTest
    80     implements Runnable {
    82     enum StaticArgumentKind {
    83         STRING("Hello!", "String", "Ljava/lang/String;") {
    84             @Override
    85             boolean check(CPInfo cpInfo) throws Exception {
    86                 return (cpInfo instanceof CONSTANT_String_info) &&
    87                         ((CONSTANT_String_info)cpInfo).getString()
    88                         .equals(value);
    89             }
    90         },
    91         CLASS(null, "Class<?>", "Ljava/lang/Class;") {
    92             @Override
    93             boolean check(CPInfo cpInfo) throws Exception {
    94                 return (cpInfo instanceof CONSTANT_Class_info) &&
    95                         ((CONSTANT_Class_info)cpInfo).getName()
    96                         .equals("java/lang/String");
    97             }
    98         },
    99         INTEGER(1, "int", "I") {
   100             @Override
   101             boolean check(CPInfo cpInfo) throws Exception {
   102                 return (cpInfo instanceof CONSTANT_Integer_info) &&
   103                         ((CONSTANT_Integer_info)cpInfo).value ==
   104                         ((Integer)value).intValue();
   105             }
   106         },
   107         LONG(1L, "long", "J") {
   108             @Override
   109             boolean check(CPInfo cpInfo) throws Exception {
   110                 return (cpInfo instanceof CONSTANT_Long_info) &&
   111                         ((CONSTANT_Long_info)cpInfo).value ==
   112                         ((Long)value).longValue();
   113             }
   114         },
   115         FLOAT(1.0f, "float", "F") {
   116             @Override
   117             boolean check(CPInfo cpInfo) throws Exception {
   118                 return (cpInfo instanceof CONSTANT_Float_info) &&
   119                         ((CONSTANT_Float_info)cpInfo).value ==
   120                         ((Float)value).floatValue();
   121             }
   122         },
   123         DOUBLE(1.0, "double","D") {
   124             @Override
   125             boolean check(CPInfo cpInfo) throws Exception {
   126                 return (cpInfo instanceof CONSTANT_Double_info) &&
   127                         ((CONSTANT_Double_info)cpInfo).value ==
   128                         ((Double)value).doubleValue();
   129             }
   130         },
   131         METHOD_HANDLE(null, "MethodHandle", "Ljava/lang/invoke/MethodHandle;") {
   132             @Override
   133             boolean check(CPInfo cpInfo) throws Exception {
   134                 if (!(cpInfo instanceof CONSTANT_MethodHandle_info))
   135                     return false;
   136                 CONSTANT_MethodHandle_info handleInfo =
   137                         (CONSTANT_MethodHandle_info)cpInfo;
   138                 return handleInfo.getCPRefInfo().getClassName().equals("Array") &&
   139                         handleInfo.reference_kind == RefKind.REF_invokeVirtual &&
   140                         handleInfo.getCPRefInfo()
   141                         .getNameAndTypeInfo().getName().equals("clone") &&
   142                         handleInfo.getCPRefInfo()
   143                         .getNameAndTypeInfo().getType().equals("()Ljava/lang/Object;");
   144             }
   145         },
   146         METHOD_TYPE(null, "MethodType", "Ljava/lang/invoke/MethodType;") {
   147             @Override
   148             boolean check(CPInfo cpInfo) throws Exception {
   149                 return (cpInfo instanceof CONSTANT_MethodType_info) &&
   150                         ((CONSTANT_MethodType_info)cpInfo).getType()
   151                         .equals("()Ljava/lang/Object;");
   152             }
   153         };
   155         Object value;
   156         String sourceTypeStr;
   157         String bytecodeTypeStr;
   159         StaticArgumentKind(Object value, String sourceTypeStr,
   160                 String bytecodeTypeStr) {
   161             this.value = value;
   162             this.sourceTypeStr = sourceTypeStr;
   163             this.bytecodeTypeStr = bytecodeTypeStr;
   164         }
   166         abstract boolean check(CPInfo cpInfo) throws Exception;
   168         Object getValue(Symtab syms, Names names, Types types) {
   169             switch (this) {
   170                 case STRING:
   171                 case INTEGER:
   172                 case LONG:
   173                 case FLOAT:
   174                 case DOUBLE:
   175                     return value;
   176                 case CLASS:
   177                     return syms.stringType.tsym;
   178                 case METHOD_HANDLE:
   179                     return new Pool.MethodHandle(REF_invokeVirtual,
   180                             syms.arrayCloneMethod, types);
   181                 case METHOD_TYPE:
   182                     return syms.arrayCloneMethod.type;
   183                 default:
   184                     throw new AssertionError();
   185             }
   186         }
   187     }
   189     enum StaticArgumentsArity {
   190         ZERO(0),
   191         ONE(1),
   192         TWO(2),
   193         THREE(3);
   195         int arity;
   197         StaticArgumentsArity(int arity) {
   198             this.arity = arity;
   199         }
   200     }
   202     public static void main(String... args) throws Exception {
   203         for (StaticArgumentsArity arity : StaticArgumentsArity.values()) {
   204             if (arity.arity == 0) {
   205                 pool.execute(new TestInvokeDynamic(arity));
   206             } else {
   207                 for (StaticArgumentKind sak1 : StaticArgumentKind.values()) {
   208                     if (arity.arity == 1) {
   209                         pool.execute(new TestInvokeDynamic(arity, sak1));
   210                     } else {
   211                         for (StaticArgumentKind sak2 : StaticArgumentKind.values()) {
   212                             if (arity.arity == 2) {
   213                                 pool.execute(new TestInvokeDynamic(arity, sak1, sak2));
   214                             } else {
   215                                 for (StaticArgumentKind sak3 : StaticArgumentKind.values()) {
   216                                     pool.execute(
   217                                         new TestInvokeDynamic(arity, sak1, sak2, sak3));
   218                                 }
   219                             }
   220                         }
   221                     }
   222                 }
   223             }
   224         }
   226         checkAfterExec();
   227     }
   229     StaticArgumentsArity arity;
   230     StaticArgumentKind[] saks;
   231     DiagChecker dc;
   233     TestInvokeDynamic(StaticArgumentsArity arity, StaticArgumentKind... saks) {
   234         this.arity = arity;
   235         this.saks = saks;
   236         dc = new DiagChecker();
   237     }
   239     public void run() {
   240         int id = checkCount.incrementAndGet();
   241         JavaSource source = new JavaSource(id);
   242         JavacTaskImpl ct = (JavacTaskImpl)comp.getTask(null, fm.get(), dc,
   243                 Arrays.asList("-g"), null, Arrays.asList(source));
   244         Context context = ct.getContext();
   245         Symtab syms = Symtab.instance(context);
   246         Names names = Names.instance(context);
   247         Types types = Types.instance(context);
   248         ct.addTaskListener(new Indifier(syms, names, types));
   249         try {
   250             ct.generate();
   251         } catch (Throwable t) {
   252             t.printStackTrace();
   253             throw new AssertionError(
   254                     String.format("Error thrown when compiling following code\n%s",
   255                     source.source));
   256         }
   257         if (dc.diagFound) {
   258             throw new AssertionError(
   259                     String.format("Diags found when compiling following code\n%s\n\n%s",
   260                     source.source, dc.printDiags()));
   261         }
   262         verifyBytecode(id);
   263     }
   265     void verifyBytecode(int id) {
   266         File compiledTest = new File(String.format("Test%d.class", id));
   267         try {
   268             ClassFile cf = ClassFile.read(compiledTest);
   269             Method testMethod = null;
   270             for (Method m : cf.methods) {
   271                 if (m.getName(cf.constant_pool).equals("test")) {
   272                     testMethod = m;
   273                     break;
   274                 }
   275             }
   276             if (testMethod == null) {
   277                 throw new Error("Test method not found");
   278             }
   279             Code_attribute ea =
   280                     (Code_attribute)testMethod.attributes.get(Attribute.Code);
   281             if (testMethod == null) {
   282                 throw new Error("Code attribute for test() method not found");
   283             }
   285             int bsmIdx = -1;
   287             for (Instruction i : ea.getInstructions()) {
   288                 if (i.getMnemonic().equals("invokedynamic")) {
   289                     CONSTANT_InvokeDynamic_info indyInfo =
   290                          (CONSTANT_InvokeDynamic_info)cf
   291                             .constant_pool.get(i.getShort(1));
   292                     bsmIdx = indyInfo.bootstrap_method_attr_index;
   293                     if (!indyInfo.getNameAndTypeInfo().getType().equals("()V")) {
   294                         throw new
   295                             AssertionError("type mismatch for CONSTANT_InvokeDynamic_info");
   296                     }
   297                 }
   298             }
   299             if (bsmIdx == -1) {
   300                 throw new Error("Missing invokedynamic in generated code");
   301             }
   303             BootstrapMethods_attribute bsm_attr =
   304                     (BootstrapMethods_attribute)cf
   305                     .getAttribute(Attribute.BootstrapMethods);
   306             if (bsm_attr.bootstrap_method_specifiers.length != 1) {
   307                 throw new Error("Bad number of method specifiers " +
   308                         "in BootstrapMethods attribute");
   309             }
   310             BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec =
   311                     bsm_attr.bootstrap_method_specifiers[0];
   313             if (bsm_spec.bootstrap_arguments.length != arity.arity) {
   314                 throw new Error("Bad number of static invokedynamic args " +
   315                         "in BootstrapMethod attribute");
   316             }
   318             int count = 0;
   319             for (StaticArgumentKind sak : saks) {
   320                 if (!sak.check(cf.constant_pool
   321                         .get(bsm_spec.bootstrap_arguments[count]))) {
   322                     throw new Error("Bad static argument value " + sak);
   323                 }
   324                 count++;
   325             }
   327             CONSTANT_MethodHandle_info bsm_handle =
   328                     (CONSTANT_MethodHandle_info)cf.constant_pool
   329                     .get(bsm_spec.bootstrap_method_ref);
   331             if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) {
   332                 throw new Error("Bad kind on boostrap method handle");
   333             }
   335             CONSTANT_Methodref_info bsm_ref =
   336                     (CONSTANT_Methodref_info)cf.constant_pool
   337                     .get(bsm_handle.reference_index);
   339             if (!bsm_ref.getClassInfo().getName().equals("Bootstrap")) {
   340                 throw new Error("Bad owner of boostrap method");
   341             }
   343             if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) {
   344                 throw new Error("Bad boostrap method name");
   345             }
   347             if (!bsm_ref.getNameAndTypeInfo()
   348                     .getType().equals(asBSMSignatureString())) {
   349                 throw new Error("Bad boostrap method type" +
   350                         bsm_ref.getNameAndTypeInfo().getType() + " " +
   351                         asBSMSignatureString());
   352             }
   354             LineNumberTable_attribute lnt =
   355                     (LineNumberTable_attribute)ea.attributes.get(Attribute.LineNumberTable);
   357             if (lnt == null) {
   358                 throw new Error("No LineNumberTable attribute");
   359             }
   360             if (lnt.line_number_table_length != 2) {
   361                 throw new Error("Wrong number of entries in LineNumberTable");
   362             }
   363         } catch (Exception e) {
   364             e.printStackTrace();
   365             throw new Error("error reading " + compiledTest +": " + e);
   366         }
   367     }
   369     String asBSMSignatureString() {
   370         StringBuilder buf = new StringBuilder();
   371         buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;");
   372         for (StaticArgumentKind sak : saks) {
   373             buf.append(sak.bytecodeTypeStr);
   374         }
   375         buf.append(")Ljava/lang/invoke/CallSite;");
   376         return buf.toString();
   377     }
   379     class JavaSource extends SimpleJavaFileObject {
   381         static final String source_template = "import java.lang.invoke.*;\n" +
   382                 "class Bootstrap {\n" +
   383                 "   public static CallSite bsm(MethodHandles.Lookup lookup, " +
   384                 "String name, MethodType methodType #SARGS) {\n" +
   385                 "       return null;\n" +
   386                 "   }\n" +
   387                 "}\n" +
   388                 "class Test#ID {\n" +
   389                 "   void m() { }\n" +
   390                 "   void test() {\n" +
   391                 "      Object o = this; // marker statement \n" +
   392                 "      m();\n" +
   393                 "   }\n" +
   394                 "}";
   396         String source;
   398         JavaSource(int id) {
   399             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
   400             source = source_template.replace("#SARGS", asSignatureString())
   401                     .replace("#ID", String.valueOf(id));
   402         }
   404         @Override
   405         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
   406             return source;
   407         }
   409         String asSignatureString() {
   410             int count = 0;
   411             StringBuilder buf = new StringBuilder();
   412             for (StaticArgumentKind sak : saks) {
   413                 buf.append(",");
   414                 buf.append(sak.sourceTypeStr);
   415                 buf.append(' ');
   416                 buf.append(String.format("x%d", count++));
   417             }
   418             return buf.toString();
   419         }
   420     }
   422     class Indifier extends TreeScanner<Void, Void> implements TaskListener {
   424         MethodSymbol bsm;
   425         Symtab syms;
   426         Names names;
   427         Types types;
   429         Indifier(Symtab syms, Names names, Types types) {
   430             this.syms = syms;
   431             this.names = names;
   432             this.types = types;
   433         }
   435         @Override
   436         public void started(TaskEvent e) {
   437             //do nothing
   438         }
   440         @Override
   441         public void finished(TaskEvent e) {
   442             if (e.getKind() == TaskEvent.Kind.ANALYZE) {
   443                 scan(e.getCompilationUnit(), null);
   444             }
   445         }
   447         @Override
   448         public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
   449             super.visitMethodInvocation(node, p);
   450             JCMethodInvocation apply = (JCMethodInvocation)node;
   451             JCIdent ident = (JCIdent)apply.meth;
   452             Symbol oldSym = ident.sym;
   453             if (!oldSym.isConstructor()) {
   454                 Object[] staticArgs = new Object[arity.arity];
   455                 for (int i = 0; i < arity.arity ; i++) {
   456                     staticArgs[i] = saks[i].getValue(syms, names, types);
   457                 }
   458                 ident.sym = new Symbol.DynamicMethodSymbol(oldSym.name,
   459                         oldSym.owner, REF_invokeStatic, bsm, oldSym.type, staticArgs);
   460             }
   461             return null;
   462         }
   464         @Override
   465         public Void visitMethod(MethodTree node, Void p) {
   466             super.visitMethod(node, p);
   467             if (node.getName().toString().equals("bsm")) {
   468                 bsm = ((JCMethodDecl)node).sym;
   469             }
   470             return null;
   471         }
   472     }
   474     static class DiagChecker
   475         implements javax.tools.DiagnosticListener<JavaFileObject> {
   477         boolean diagFound;
   478         ArrayList<String> diags = new ArrayList<>();
   480         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   481             diags.add(diagnostic.getMessage(Locale.getDefault()));
   482             diagFound = true;
   483         }
   485         String printDiags() {
   486             StringBuilder buf = new StringBuilder();
   487             for (String s : diags) {
   488                 buf.append(s);
   489                 buf.append("\n");
   490             }
   491             return buf.toString();
   492         }
   493     }
   495 }

mercurial