test/tools/javac/lambda/TestInvokeDynamic.java

Tue, 08 Jan 2013 13:47:57 +0000

author
vromero
date
Tue, 08 Jan 2013 13:47:57 +0000
changeset 1482
954541f13717
parent 1452
de1ec6fc93fe
child 1520
5c956be64b9e
permissions
-rw-r--r--

8005167: execution time of combo tests in javac should be improved
Reviewed-by: jjg, jjh

     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  *
    28  * @bug 8003280
    29  * @summary Add lambda tests
    30  *  Add back-end support for invokedynamic
    31  * @library ../lib
    32  * @build JavacTestingAbstractThreadedTest
    33  * @run main TestInvokeDynamic
    34  */
    36 import com.sun.source.tree.MethodInvocationTree;
    37 import com.sun.source.tree.MethodTree;
    38 import com.sun.source.util.TaskEvent;
    39 import com.sun.source.util.TaskListener;
    40 import com.sun.source.util.TreeScanner;
    42 import com.sun.tools.classfile.Attribute;
    43 import com.sun.tools.classfile.BootstrapMethods_attribute;
    44 import com.sun.tools.classfile.ClassFile;
    45 import com.sun.tools.classfile.Code_attribute;
    46 import com.sun.tools.classfile.ConstantPool.*;
    47 import com.sun.tools.classfile.Instruction;
    48 import com.sun.tools.classfile.Method;
    50 import com.sun.tools.javac.api.JavacTaskImpl;
    51 import com.sun.tools.javac.api.JavacTool;
    52 import com.sun.tools.javac.code.Symbol;
    53 import com.sun.tools.javac.code.Symbol.MethodSymbol;
    54 import com.sun.tools.javac.code.Symtab;
    55 import com.sun.tools.javac.code.Types;
    56 import com.sun.tools.javac.jvm.Pool;
    57 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
    58 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
    59 import com.sun.tools.javac.tree.JCTree.JCIdent;
    60 import com.sun.tools.javac.util.Context;
    61 import com.sun.tools.javac.util.Names;
    63 import java.io.File;
    64 import java.net.URI;
    65 import java.util.ArrayList;
    66 import java.util.Arrays;
    67 import java.util.Locale;
    69 import javax.tools.Diagnostic;
    70 import javax.tools.JavaCompiler;
    71 import javax.tools.JavaFileObject;
    72 import javax.tools.SimpleJavaFileObject;
    73 import javax.tools.StandardJavaFileManager;
    74 import javax.tools.ToolProvider;
    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                 null, 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             }
   353         } catch (Exception e) {
   354             e.printStackTrace();
   355             throw new Error("error reading " + compiledTest +": " + e);
   356         }
   357     }
   359     String asBSMSignatureString() {
   360         StringBuilder buf = new StringBuilder();
   361         buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;");
   362         for (StaticArgumentKind sak : saks) {
   363             buf.append(sak.bytecodeTypeStr);
   364         }
   365         buf.append(")Ljava/lang/invoke/CallSite;");
   366         return buf.toString();
   367     }
   369     class JavaSource extends SimpleJavaFileObject {
   371         static final String source_template = "import java.lang.invoke.*;\n" +
   372                 "class Bootstrap {\n" +
   373                 "   public static CallSite bsm(MethodHandles.Lookup lookup, " +
   374                 "String name, MethodType methodType #SARGS) {\n" +
   375                 "       return null;\n" +
   376                 "   }\n" +
   377                 "}\n" +
   378                 "class Test#ID {\n" +
   379                 "   void m() { }\n" +
   380                 "   void test() { m(); }\n" +
   381                 "}";
   383         String source;
   385         JavaSource(int id) {
   386             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
   387             source = source_template.replace("#SARGS", asSignatureString())
   388                     .replace("#ID", String.valueOf(id));
   389         }
   391         @Override
   392         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
   393             return source;
   394         }
   396         String asSignatureString() {
   397             int count = 0;
   398             StringBuilder buf = new StringBuilder();
   399             for (StaticArgumentKind sak : saks) {
   400                 buf.append(",");
   401                 buf.append(sak.sourceTypeStr);
   402                 buf.append(' ');
   403                 buf.append(String.format("x%d", count++));
   404             }
   405             return buf.toString();
   406         }
   407     }
   409     class Indifier extends TreeScanner<Void, Void> implements TaskListener {
   411         MethodSymbol bsm;
   412         Symtab syms;
   413         Names names;
   414         Types types;
   416         Indifier(Symtab syms, Names names, Types types) {
   417             this.syms = syms;
   418             this.names = names;
   419             this.types = types;
   420         }
   422         @Override
   423         public void started(TaskEvent e) {
   424             //do nothing
   425         }
   427         @Override
   428         public void finished(TaskEvent e) {
   429             if (e.getKind() == TaskEvent.Kind.ANALYZE) {
   430                 scan(e.getCompilationUnit(), null);
   431             }
   432         }
   434         @Override
   435         public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
   436             super.visitMethodInvocation(node, p);
   437             JCMethodInvocation apply = (JCMethodInvocation)node;
   438             JCIdent ident = (JCIdent)apply.meth;
   439             Symbol oldSym = ident.sym;
   440             if (!oldSym.isConstructor()) {
   441                 Object[] staticArgs = new Object[arity.arity];
   442                 for (int i = 0; i < arity.arity ; i++) {
   443                     staticArgs[i] = saks[i].getValue(syms, names, types);
   444                 }
   445                 ident.sym = new Symbol.DynamicMethodSymbol(oldSym.name,
   446                         oldSym.owner, REF_invokeStatic, bsm, oldSym.type, staticArgs);
   447             }
   448             return null;
   449         }
   451         @Override
   452         public Void visitMethod(MethodTree node, Void p) {
   453             super.visitMethod(node, p);
   454             if (node.getName().toString().equals("bsm")) {
   455                 bsm = ((JCMethodDecl)node).sym;
   456             }
   457             return null;
   458         }
   459     }
   461     static class DiagChecker
   462         implements javax.tools.DiagnosticListener<JavaFileObject> {
   464         boolean diagFound;
   465         ArrayList<String> diags = new ArrayList<>();
   467         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   468             diags.add(diagnostic.getMessage(Locale.getDefault()));
   469             diagFound = true;
   470         }
   472         String printDiags() {
   473             StringBuilder buf = new StringBuilder();
   474             for (String s : diags) {
   475                 buf.append(s);
   476                 buf.append("\n");
   477             }
   478             return buf.toString();
   479         }
   480     }
   482 }

mercurial