test/tools/javac/lambda/TestInvokeDynamic.java

Tue, 12 Mar 2013 16:02:43 +0000

author
mcimadamore
date
Tue, 12 Mar 2013 16:02:43 +0000
changeset 1628
5ddecb91d843
parent 1520
5c956be64b9e
child 1676
e9d986381414
permissions
-rw-r--r--

8009545: Graph inference: dependencies between inference variables should be set during incorporation
Summary: Move all transitivity checks into the incorporation round
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
    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.Method;
    53 import com.sun.tools.javac.api.JavacTaskImpl;
    54 import com.sun.tools.javac.code.Symbol;
    55 import com.sun.tools.javac.code.Symbol.MethodSymbol;
    56 import com.sun.tools.javac.code.Symtab;
    57 import com.sun.tools.javac.code.Types;
    58 import com.sun.tools.javac.jvm.Pool;
    59 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
    60 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
    61 import com.sun.tools.javac.tree.JCTree.JCIdent;
    62 import com.sun.tools.javac.util.Context;
    63 import com.sun.tools.javac.util.Names;
    65 import java.io.File;
    66 import java.net.URI;
    67 import java.util.ArrayList;
    68 import java.util.Arrays;
    69 import java.util.Locale;
    71 import javax.tools.Diagnostic;
    72 import javax.tools.JavaFileObject;
    73 import javax.tools.SimpleJavaFileObject;
    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                 null, 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             }
   352         } catch (Exception e) {
   353             e.printStackTrace();
   354             throw new Error("error reading " + compiledTest +": " + e);
   355         }
   356     }
   358     String asBSMSignatureString() {
   359         StringBuilder buf = new StringBuilder();
   360         buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;");
   361         for (StaticArgumentKind sak : saks) {
   362             buf.append(sak.bytecodeTypeStr);
   363         }
   364         buf.append(")Ljava/lang/invoke/CallSite;");
   365         return buf.toString();
   366     }
   368     class JavaSource extends SimpleJavaFileObject {
   370         static final String source_template = "import java.lang.invoke.*;\n" +
   371                 "class Bootstrap {\n" +
   372                 "   public static CallSite bsm(MethodHandles.Lookup lookup, " +
   373                 "String name, MethodType methodType #SARGS) {\n" +
   374                 "       return null;\n" +
   375                 "   }\n" +
   376                 "}\n" +
   377                 "class Test#ID {\n" +
   378                 "   void m() { }\n" +
   379                 "   void test() { m(); }\n" +
   380                 "}";
   382         String source;
   384         JavaSource(int id) {
   385             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
   386             source = source_template.replace("#SARGS", asSignatureString())
   387                     .replace("#ID", String.valueOf(id));
   388         }
   390         @Override
   391         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
   392             return source;
   393         }
   395         String asSignatureString() {
   396             int count = 0;
   397             StringBuilder buf = new StringBuilder();
   398             for (StaticArgumentKind sak : saks) {
   399                 buf.append(",");
   400                 buf.append(sak.sourceTypeStr);
   401                 buf.append(' ');
   402                 buf.append(String.format("x%d", count++));
   403             }
   404             return buf.toString();
   405         }
   406     }
   408     class Indifier extends TreeScanner<Void, Void> implements TaskListener {
   410         MethodSymbol bsm;
   411         Symtab syms;
   412         Names names;
   413         Types types;
   415         Indifier(Symtab syms, Names names, Types types) {
   416             this.syms = syms;
   417             this.names = names;
   418             this.types = types;
   419         }
   421         @Override
   422         public void started(TaskEvent e) {
   423             //do nothing
   424         }
   426         @Override
   427         public void finished(TaskEvent e) {
   428             if (e.getKind() == TaskEvent.Kind.ANALYZE) {
   429                 scan(e.getCompilationUnit(), null);
   430             }
   431         }
   433         @Override
   434         public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
   435             super.visitMethodInvocation(node, p);
   436             JCMethodInvocation apply = (JCMethodInvocation)node;
   437             JCIdent ident = (JCIdent)apply.meth;
   438             Symbol oldSym = ident.sym;
   439             if (!oldSym.isConstructor()) {
   440                 Object[] staticArgs = new Object[arity.arity];
   441                 for (int i = 0; i < arity.arity ; i++) {
   442                     staticArgs[i] = saks[i].getValue(syms, names, types);
   443                 }
   444                 ident.sym = new Symbol.DynamicMethodSymbol(oldSym.name,
   445                         oldSym.owner, REF_invokeStatic, bsm, oldSym.type, staticArgs);
   446             }
   447             return null;
   448         }
   450         @Override
   451         public Void visitMethod(MethodTree node, Void p) {
   452             super.visitMethod(node, p);
   453             if (node.getName().toString().equals("bsm")) {
   454                 bsm = ((JCMethodDecl)node).sym;
   455             }
   456             return null;
   457         }
   458     }
   460     static class DiagChecker
   461         implements javax.tools.DiagnosticListener<JavaFileObject> {
   463         boolean diagFound;
   464         ArrayList<String> diags = new ArrayList<>();
   466         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   467             diags.add(diagnostic.getMessage(Locale.getDefault()));
   468             diagFound = true;
   469         }
   471         String printDiags() {
   472             StringBuilder buf = new StringBuilder();
   473             for (String s : diags) {
   474                 buf.append(s);
   475                 buf.append("\n");
   476             }
   477             return buf.toString();
   478         }
   479     }
   481 }

mercurial