test/tools/javac/lambda/bytecode/TestLambdaBytecode.java

Wed, 27 Apr 2016 01:34:52 +0800

author
aoqi
date
Wed, 27 Apr 2016 01:34:52 +0800
changeset 0
959103a6100f
child 2525
2eb010b6cb22
permissions
-rw-r--r--

Initial load
http://hg.openjdk.java.net/jdk8u/jdk8u/langtools/
changeset: 2573:53ca196be1ae
tag: jdk8u25-b17

     1 /*
     2  * Copyright (c) 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 8009649
    27  * @summary Lambda back-end should generate invokespecial for method handles referring to private instance methods
    28  * @library ../../lib
    29  * @build JavacTestingAbstractThreadedTest
    30  * @run main/othervm TestLambdaBytecode
    31  */
    33 // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
    34 // see JDK-8006746
    36 import com.sun.tools.classfile.Attribute;
    37 import com.sun.tools.classfile.BootstrapMethods_attribute;
    38 import com.sun.tools.classfile.ClassFile;
    39 import com.sun.tools.classfile.Code_attribute;
    40 import com.sun.tools.classfile.ConstantPool.*;
    41 import com.sun.tools.classfile.Instruction;
    42 import com.sun.tools.classfile.Method;
    44 import com.sun.tools.javac.api.JavacTaskImpl;
    47 import java.io.File;
    48 import java.net.URI;
    49 import java.util.ArrayList;
    50 import java.util.Arrays;
    51 import java.util.Locale;
    53 import javax.tools.Diagnostic;
    54 import javax.tools.JavaFileObject;
    55 import javax.tools.SimpleJavaFileObject;
    57 import static com.sun.tools.javac.jvm.ClassFile.*;
    59 public class TestLambdaBytecode
    60     extends JavacTestingAbstractThreadedTest
    61     implements Runnable {
    63     enum ClassKind {
    64         CLASS("class"),
    65         INTERFACE("interface");
    67         String classStr;
    69         ClassKind(String classStr) {
    70             this.classStr = classStr;
    71         }
    72     }
    74     enum AccessKind {
    75         PUBLIC("public"),
    76         PRIVATE("private");
    78         String accessStr;
    80         AccessKind(String accessStr) {
    81             this.accessStr = accessStr;
    82         }
    83     }
    85     enum StaticKind {
    86         STATIC("static"),
    87         INSTANCE("");
    89         String staticStr;
    91         StaticKind(String staticStr) {
    92             this.staticStr = staticStr;
    93         }
    94     }
    96     enum DefaultKind {
    97         DEFAULT("default"),
    98         NO_DEFAULT("");
   100         String defaultStr;
   102         DefaultKind(String defaultStr) {
   103             this.defaultStr = defaultStr;
   104         }
   105     }
   107     enum ExprKind {
   108         LAMBDA("Runnable r = ()->{ target(); };");
   110         String exprString;
   112         ExprKind(String exprString) {
   113             this.exprString = exprString;
   114         }
   115     }
   117     static class MethodKind {
   118         ClassKind ck;
   119         AccessKind ak;
   120         StaticKind sk;
   121         DefaultKind dk;
   123         MethodKind(ClassKind ck, AccessKind ak, StaticKind sk, DefaultKind dk) {
   124             this.ck = ck;
   125             this.ak = ak;
   126             this.sk = sk;
   127             this.dk = dk;
   128         }
   130         boolean inInterface() {
   131             return ck == ClassKind.INTERFACE;
   132         }
   134         boolean isPrivate() {
   135             return ak == AccessKind.PRIVATE;
   136         }
   138         boolean isStatic() {
   139             return sk == StaticKind.STATIC;
   140         }
   142         boolean isDefault() {
   143             return dk == DefaultKind.DEFAULT;
   144         }
   146         boolean isOK() {
   147             if (isDefault() && (!inInterface() || isStatic())) {
   148                 return false;
   149             } else if (inInterface() &&
   150                     ((!isStatic() && !isDefault()) || isPrivate())) {
   151                 return false;
   152             } else {
   153                 return true;
   154             }
   155         }
   157         String mods() {
   158             StringBuilder buf = new StringBuilder();
   159             buf.append(ak.accessStr);
   160             buf.append(' ');
   161             buf.append(sk.staticStr);
   162             buf.append(' ');
   163             buf.append(dk.defaultStr);
   164             return buf.toString();
   165         }
   166     }
   168     public static void main(String... args) throws Exception {
   169         for (ClassKind ck : ClassKind.values()) {
   170             for (AccessKind ak1 : AccessKind.values()) {
   171                 for (StaticKind sk1 : StaticKind.values()) {
   172                     for (DefaultKind dk1 : DefaultKind.values()) {
   173                         for (AccessKind ak2 : AccessKind.values()) {
   174                             for (StaticKind sk2 : StaticKind.values()) {
   175                                 for (DefaultKind dk2 : DefaultKind.values()) {
   176                                     for (ExprKind ek : ExprKind.values()) {
   177                                         pool.execute(new TestLambdaBytecode(ck, ak1, ak2, sk1, sk2, dk1, dk2, ek));
   178                                     }
   179                                 }
   180                             }
   181                         }
   182                     }
   183                 }
   184             }
   185         }
   187         checkAfterExec();
   188     }
   190     MethodKind mk1, mk2;
   191     ExprKind ek;
   192     DiagChecker dc;
   194     TestLambdaBytecode(ClassKind ck, AccessKind ak1, AccessKind ak2, StaticKind sk1,
   195             StaticKind sk2, DefaultKind dk1, DefaultKind dk2, ExprKind ek) {
   196         mk1 = new MethodKind(ck, ak1, sk1, dk1);
   197         mk2 = new MethodKind(ck, ak2, sk2, dk2);
   198         this.ek = ek;
   199         dc = new DiagChecker();
   200     }
   202     public void run() {
   203         int id = checkCount.incrementAndGet();
   204         JavaSource source = new JavaSource(id);
   205         JavacTaskImpl ct = (JavacTaskImpl)comp.getTask(null, fm.get(), dc,
   206                 null, null, Arrays.asList(source));
   207         try {
   208             ct.generate();
   209         } catch (Throwable t) {
   210             t.printStackTrace();
   211             throw new AssertionError(
   212                     String.format("Error thrown when compiling following code\n%s",
   213                     source.source));
   214         }
   215         if (dc.diagFound) {
   216             boolean errorExpected = !mk1.isOK() || !mk2.isOK();
   217             errorExpected |= mk1.isStatic() && !mk2.isStatic();
   219             if (!errorExpected) {
   220                 throw new AssertionError(
   221                         String.format("Diags found when compiling following code\n%s\n\n%s",
   222                         source.source, dc.printDiags()));
   223             }
   224             return;
   225         }
   226         verifyBytecode(id, source);
   227     }
   229     void verifyBytecode(int id, JavaSource source) {
   230         File compiledTest = new File(String.format("Test%d.class", id));
   231         try {
   232             ClassFile cf = ClassFile.read(compiledTest);
   233             Method testMethod = null;
   234             for (Method m : cf.methods) {
   235                 if (m.getName(cf.constant_pool).equals("test")) {
   236                     testMethod = m;
   237                     break;
   238                 }
   239             }
   240             if (testMethod == null) {
   241                 throw new Error("Test method not found");
   242             }
   243             Code_attribute ea =
   244                     (Code_attribute)testMethod.attributes.get(Attribute.Code);
   245             if (testMethod == null) {
   246                 throw new Error("Code attribute for test() method not found");
   247             }
   249             int bsmIdx = -1;
   251             for (Instruction i : ea.getInstructions()) {
   252                 if (i.getMnemonic().equals("invokedynamic")) {
   253                     CONSTANT_InvokeDynamic_info indyInfo =
   254                          (CONSTANT_InvokeDynamic_info)cf
   255                             .constant_pool.get(i.getShort(1));
   256                     bsmIdx = indyInfo.bootstrap_method_attr_index;
   257                     if (!indyInfo.getNameAndTypeInfo().getType().equals(makeIndyType(id))) {
   258                         throw new
   259                             AssertionError("type mismatch for CONSTANT_InvokeDynamic_info " + source.source + "\n" + indyInfo.getNameAndTypeInfo().getType() + "\n" + makeIndyType(id));
   260                     }
   261                 }
   262             }
   263             if (bsmIdx == -1) {
   264                 throw new Error("Missing invokedynamic in generated code");
   265             }
   267             BootstrapMethods_attribute bsm_attr =
   268                     (BootstrapMethods_attribute)cf
   269                     .getAttribute(Attribute.BootstrapMethods);
   270             if (bsm_attr.bootstrap_method_specifiers.length != 1) {
   271                 throw new Error("Bad number of method specifiers " +
   272                         "in BootstrapMethods attribute");
   273             }
   274             BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec =
   275                     bsm_attr.bootstrap_method_specifiers[0];
   277             if (bsm_spec.bootstrap_arguments.length != MF_ARITY) {
   278                 throw new Error("Bad number of static invokedynamic args " +
   279                         "in BootstrapMethod attribute");
   280             }
   282             CONSTANT_MethodHandle_info mh =
   283                     (CONSTANT_MethodHandle_info)cf.constant_pool.get(bsm_spec.bootstrap_arguments[1]);
   285             boolean kindOK;
   286             switch (mh.reference_kind) {
   287                 case REF_invokeStatic: kindOK = mk2.isStatic(); break;
   288                 case REF_invokeSpecial: kindOK = !mk2.isStatic(); break;
   289                 case REF_invokeInterface: kindOK = mk2.inInterface(); break;
   290                 default:
   291                     kindOK = false;
   292             }
   294             if (!kindOK) {
   295                 throw new Error("Bad invoke kind in implementation method handle");
   296             }
   298             if (!mh.getCPRefInfo().getNameAndTypeInfo().getType().toString().equals(MH_SIG)) {
   299                 throw new Error("Type mismatch in implementation method handle");
   300             }
   301         } catch (Exception e) {
   302             e.printStackTrace();
   303             throw new Error("error reading " + compiledTest +": " + e);
   304         }
   305     }
   306     String makeIndyType(int id) {
   307         StringBuilder buf = new StringBuilder();
   308         buf.append("(");
   309         if (!mk2.isStatic()) {
   310             buf.append(String.format("LTest%d;", id));
   311         }
   312         buf.append(")Ljava/lang/Runnable;");
   313         return buf.toString();
   314     }
   316     static final int MF_ARITY = 3;
   317     static final String MH_SIG = "()V";
   319     class JavaSource extends SimpleJavaFileObject {
   321         static final String source_template =
   322                 "#CK Test#ID {\n" +
   323                 "   #MOD1 void test() { #EK }\n" +
   324                 "   #MOD2 void target() { }\n" +
   325                 "}\n";
   327         String source;
   329         JavaSource(int id) {
   330             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
   331             source = source_template.replace("#CK", mk1.ck.classStr)
   332                     .replace("#MOD1", mk1.mods())
   333                     .replace("#MOD2", mk2.mods())
   334                     .replace("#EK", ek.exprString)
   335                     .replace("#ID", String.valueOf(id));
   336         }
   338         @Override
   339         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
   340             return source;
   341         }
   342     }
   344     static class DiagChecker
   345         implements javax.tools.DiagnosticListener<JavaFileObject> {
   347         boolean diagFound;
   348         ArrayList<String> diags = new ArrayList<>();
   350         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   351             diags.add(diagnostic.getMessage(Locale.getDefault()));
   352             diagFound = true;
   353         }
   355         String printDiags() {
   356             StringBuilder buf = new StringBuilder();
   357             for (String s : diags) {
   358                 buf.append(s);
   359                 buf.append("\n");
   360             }
   361             return buf.toString();
   362         }
   363     }
   365 }

mercurial