langtools/test/tools/javac/lambda/bytecode/TestLambdaBytecode.java
changeset 32454 b0ac04e0fefe
parent 30846 2b3f379840f0
equal deleted inserted replaced
32453:8eebd1f0b8ea 32454:b0ac04e0fefe
    21  * questions.
    21  * questions.
    22  */
    22  */
    23 
    23 
    24 /*
    24 /*
    25  * @test
    25  * @test
    26  * @bug 8009649
    26  * @bug 8009649 8129962
    27  * @summary Lambda back-end should generate invokespecial for method handles referring to private instance methods
    27  * @summary Lambda back-end should generate invokespecial for method handles referring to private instance methods
    28  * @library ../../lib
    28  * @library /tools/javac/lib
    29  * @modules jdk.jdeps/com.sun.tools.classfile
    29  * @modules jdk.jdeps/com.sun.tools.classfile
    30  *          jdk.compiler/com.sun.tools.javac.api
    30  *          jdk.compiler/com.sun.tools.javac.api
    31  * @build JavacTestingAbstractThreadedTest
    31  *          jdk.compiler/com.sun.tools.javac.code
    32  * @run main/othervm TestLambdaBytecode
    32  *          jdk.compiler/com.sun.tools.javac.comp
       
    33  *          jdk.compiler/com.sun.tools.javac.main
       
    34  *          jdk.compiler/com.sun.tools.javac.tree
       
    35  *          jdk.compiler/com.sun.tools.javac.util
       
    36  * @build combo.ComboTestHelper
       
    37  * @run main TestLambdaBytecode
    33  */
    38  */
    34 
       
    35 // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
       
    36 // see JDK-8006746
       
    37 
    39 
    38 import com.sun.tools.classfile.Attribute;
    40 import com.sun.tools.classfile.Attribute;
    39 import com.sun.tools.classfile.BootstrapMethods_attribute;
    41 import com.sun.tools.classfile.BootstrapMethods_attribute;
    40 import com.sun.tools.classfile.ClassFile;
    42 import com.sun.tools.classfile.ClassFile;
    41 import com.sun.tools.classfile.Code_attribute;
    43 import com.sun.tools.classfile.Code_attribute;
    42 import com.sun.tools.classfile.ConstantPool.*;
    44 import com.sun.tools.classfile.ConstantPool.*;
    43 import com.sun.tools.classfile.Instruction;
    45 import com.sun.tools.classfile.Instruction;
    44 import com.sun.tools.classfile.Method;
    46 import com.sun.tools.classfile.Method;
    45 
    47 
    46 import com.sun.tools.javac.api.JavacTaskImpl;
    48 import java.io.IOException;
    47 
    49 import java.io.InputStream;
    48 
    50 
    49 import java.io.File;
    51 import combo.ComboInstance;
    50 import java.net.URI;
    52 import combo.ComboParameter;
    51 import java.util.ArrayList;
    53 import combo.ComboTask.Result;
    52 import java.util.Arrays;
    54 import combo.ComboTestHelper;
    53 import java.util.Locale;
    55 
    54 
       
    55 import javax.tools.Diagnostic;
       
    56 import javax.tools.JavaFileObject;
    56 import javax.tools.JavaFileObject;
    57 import javax.tools.SimpleJavaFileObject;
    57 
    58 
    58 public class TestLambdaBytecode extends ComboInstance<TestLambdaBytecode> {
    59 import static com.sun.tools.javac.jvm.ClassFile.*;
    59 
    60 
    60     static final int MF_ARITY = 3;
    61 public class TestLambdaBytecode
    61     static final String MH_SIG = "()V";
    62     extends JavacTestingAbstractThreadedTest
    62 
    63     implements Runnable {
    63     enum ClassKind implements ComboParameter {
    64 
       
    65     enum ClassKind {
       
    66         CLASS("class"),
    64         CLASS("class"),
    67         INTERFACE("interface");
    65         INTERFACE("interface");
    68 
    66 
    69         String classStr;
    67         String classStr;
    70 
    68 
    71         ClassKind(String classStr) {
    69         ClassKind(String classStr) {
    72             this.classStr = classStr;
    70             this.classStr = classStr;
    73         }
    71         }
    74     }
    72 
    75 
    73         @Override
    76     enum AccessKind {
    74         public String expand(String optParameter) {
       
    75             return classStr;
       
    76         }
       
    77     }
       
    78 
       
    79     enum AccessKind implements ComboParameter {
    77         PUBLIC("public"),
    80         PUBLIC("public"),
    78         PRIVATE("private");
    81         PRIVATE("private");
    79 
    82 
    80         String accessStr;
    83         String accessStr;
    81 
    84 
    82         AccessKind(String accessStr) {
    85         AccessKind(String accessStr) {
    83             this.accessStr = accessStr;
    86             this.accessStr = accessStr;
    84         }
    87         }
    85     }
    88 
    86 
    89         @Override
    87     enum StaticKind {
    90         public String expand(String optParameter) {
       
    91             return accessStr;
       
    92         }
       
    93     }
       
    94 
       
    95     enum StaticKind implements ComboParameter {
    88         STATIC("static"),
    96         STATIC("static"),
    89         INSTANCE("");
    97         INSTANCE("");
    90 
    98 
    91         String staticStr;
    99         String staticStr;
    92 
   100 
    93         StaticKind(String staticStr) {
   101         StaticKind(String staticStr) {
    94             this.staticStr = staticStr;
   102             this.staticStr = staticStr;
    95         }
   103         }
    96     }
   104 
    97 
   105         @Override
    98     enum DefaultKind {
   106         public String expand(String optParameter) {
       
   107             return staticStr;
       
   108         }
       
   109     }
       
   110 
       
   111     enum DefaultKind implements ComboParameter {
    99         DEFAULT("default"),
   112         DEFAULT("default"),
   100         NO_DEFAULT("");
   113         NO_DEFAULT("");
   101 
   114 
   102         String defaultStr;
   115         String defaultStr;
   103 
   116 
   104         DefaultKind(String defaultStr) {
   117         DefaultKind(String defaultStr) {
   105             this.defaultStr = defaultStr;
   118             this.defaultStr = defaultStr;
   106         }
   119         }
   107     }
   120 
   108 
   121         @Override
   109     enum ExprKind {
   122         public String expand(String optParameter) {
   110         LAMBDA("Runnable r = ()->{ target(); };");
   123             return defaultStr;
   111 
       
   112         String exprString;
       
   113 
       
   114         ExprKind(String exprString) {
       
   115             this.exprString = exprString;
       
   116         }
   124         }
   117     }
   125     }
   118 
   126 
   119     static class MethodKind {
   127     static class MethodKind {
   120         ClassKind ck;
   128         ClassKind ck;
   153                 return false;
   161                 return false;
   154             } else {
   162             } else {
   155                 return true;
   163                 return true;
   156             }
   164             }
   157         }
   165         }
   158 
       
   159         String mods() {
       
   160             StringBuilder buf = new StringBuilder();
       
   161             buf.append(ak.accessStr);
       
   162             buf.append(' ');
       
   163             buf.append(sk.staticStr);
       
   164             buf.append(' ');
       
   165             buf.append(dk.defaultStr);
       
   166             return buf.toString();
       
   167         }
       
   168     }
   166     }
   169 
   167 
   170     public static void main(String... args) throws Exception {
   168     public static void main(String... args) throws Exception {
   171         for (ClassKind ck : ClassKind.values()) {
   169         new ComboTestHelper<TestLambdaBytecode>()
   172             for (AccessKind ak1 : AccessKind.values()) {
   170                 .withDimension("CLASSKIND", (x, ck) -> x.ck = ck, ClassKind.values())
   173                 for (StaticKind sk1 : StaticKind.values()) {
   171                 .withArrayDimension("ACCESS", (x, acc, idx) -> x.accessKinds[idx] = acc, 2, AccessKind.values())
   174                     for (DefaultKind dk1 : DefaultKind.values()) {
   172                 .withArrayDimension("STATIC", (x, sk, idx) -> x.staticKinds[idx] = sk, 2, StaticKind.values())
   175                         for (AccessKind ak2 : AccessKind.values()) {
   173                 .withArrayDimension("DEFAULT", (x, dk, idx) -> x.defaultKinds[idx] = dk, 2, DefaultKind.values())
   176                             for (StaticKind sk2 : StaticKind.values()) {
   174                 .run(TestLambdaBytecode::new, TestLambdaBytecode::init);
   177                                 for (DefaultKind dk2 : DefaultKind.values()) {
   175     }
   178                                     for (ExprKind ek : ExprKind.values()) {
   176 
   179                                         pool.execute(new TestLambdaBytecode(ck, ak1, ak2, sk1, sk2, dk1, dk2, ek));
   177     ClassKind ck;
   180                                     }
   178     AccessKind[] accessKinds = new AccessKind[2];
   181                                 }
   179     StaticKind[] staticKinds = new StaticKind[2];
   182                             }
   180     DefaultKind[] defaultKinds = new DefaultKind[2];
   183                         }
       
   184                     }
       
   185                 }
       
   186             }
       
   187         }
       
   188 
       
   189         checkAfterExec();
       
   190     }
       
   191 
       
   192     MethodKind mk1, mk2;
   181     MethodKind mk1, mk2;
   193     ExprKind ek;
   182 
   194     DiagChecker dc;
   183     void init() {
   195 
   184         mk1 = new MethodKind(ck, accessKinds[0], staticKinds[0], defaultKinds[0]);
   196     TestLambdaBytecode(ClassKind ck, AccessKind ak1, AccessKind ak2, StaticKind sk1,
   185         mk2 = new MethodKind(ck, accessKinds[1], staticKinds[1], defaultKinds[1]);
   197             StaticKind sk2, DefaultKind dk1, DefaultKind dk2, ExprKind ek) {
   186     }
   198         mk1 = new MethodKind(ck, ak1, sk1, dk1);
   187 
   199         mk2 = new MethodKind(ck, ak2, sk2, dk2);
   188     String source_template =
   200         this.ek = ek;
   189                 "#{CLASSKIND} Test {\n" +
   201         dc = new DiagChecker();
   190                 "   #{ACCESS[0]} #{STATIC[0]} #{DEFAULT[0]} void test() { Runnable r = ()->{ target(); }; }\n" +
   202     }
   191                 "   #{ACCESS[1]} #{STATIC[1]} #{DEFAULT[1]} void target() { }\n" +
   203 
   192                 "}\n";
   204     public void run() {
   193 
   205         int id = checkCount.incrementAndGet();
   194     @Override
   206         JavaSource source = new JavaSource(id);
   195     public void doWork() throws IOException {
   207         JavacTaskImpl ct = (JavacTaskImpl)comp.getTask(null, fm.get(), dc,
   196         verifyBytecode(newCompilationTask()
   208                 null, null, Arrays.asList(source));
   197                 .withSourceFromTemplate(source_template)
   209         try {
   198                 .generate());
   210             ct.generate();
   199     }
   211         } catch (Throwable t) {
   200 
   212             t.printStackTrace();
   201     void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) {
   213             throw new AssertionError(
   202         if (res.hasErrors()) {
   214                     String.format("Error thrown when compiling following code\n%s",
       
   215                     source.source));
       
   216         }
       
   217         if (dc.diagFound) {
       
   218             boolean errorExpected = !mk1.isOK() || !mk2.isOK();
   203             boolean errorExpected = !mk1.isOK() || !mk2.isOK();
   219             errorExpected |= mk1.isStatic() && !mk2.isStatic();
   204             errorExpected |= mk1.isStatic() && !mk2.isStatic();
   220 
   205 
   221             if (!errorExpected) {
   206             if (!errorExpected) {
   222                 throw new AssertionError(
   207                 fail("Diags found when compiling instance; " + res.compilationInfo());
   223                         String.format("Diags found when compiling following code\n%s\n\n%s",
       
   224                         source.source, dc.printDiags()));
       
   225             }
   208             }
   226             return;
   209             return;
   227         }
   210         }
   228         verifyBytecode(id, source);
   211         try (InputStream is = res.get().iterator().next().openInputStream()) {
   229     }
   212             ClassFile cf = ClassFile.read(is);
   230 
       
   231     void verifyBytecode(int id, JavaSource source) {
       
   232         File compiledTest = new File(String.format("Test%d.class", id));
       
   233         try {
       
   234             ClassFile cf = ClassFile.read(compiledTest);
       
   235             Method testMethod = null;
   213             Method testMethod = null;
   236             for (Method m : cf.methods) {
   214             for (Method m : cf.methods) {
   237                 if (m.getName(cf.constant_pool).equals("test")) {
   215                 if (m.getName(cf.constant_pool).equals("test")) {
   238                     testMethod = m;
   216                     testMethod = m;
   239                     break;
   217                     break;
   240                 }
   218                 }
   241             }
   219             }
   242             if (testMethod == null) {
   220             if (testMethod == null) {
   243                 throw new Error("Test method not found");
   221                 fail("Test method not found");
       
   222                 return;
   244             }
   223             }
   245             Code_attribute ea =
   224             Code_attribute ea =
   246                     (Code_attribute)testMethod.attributes.get(Attribute.Code);
   225                     (Code_attribute)testMethod.attributes.get(Attribute.Code);
   247             if (testMethod == null) {
   226             if (testMethod == null) {
   248                 throw new Error("Code attribute for test() method not found");
   227                 fail("Code attribute for test() method not found");
       
   228                 return;
   249             }
   229             }
   250 
   230 
   251             int bsmIdx = -1;
   231             int bsmIdx = -1;
   252 
   232 
   253             for (Instruction i : ea.getInstructions()) {
   233             for (Instruction i : ea.getInstructions()) {
   254                 if (i.getMnemonic().equals("invokedynamic")) {
   234                 if (i.getMnemonic().equals("invokedynamic")) {
   255                     CONSTANT_InvokeDynamic_info indyInfo =
   235                     CONSTANT_InvokeDynamic_info indyInfo =
   256                          (CONSTANT_InvokeDynamic_info)cf
   236                          (CONSTANT_InvokeDynamic_info)cf
   257                             .constant_pool.get(i.getShort(1));
   237                             .constant_pool.get(i.getShort(1));
   258                     bsmIdx = indyInfo.bootstrap_method_attr_index;
   238                     bsmIdx = indyInfo.bootstrap_method_attr_index;
   259                     if (!indyInfo.getNameAndTypeInfo().getType().equals(makeIndyType(id))) {
   239                     if (!indyInfo.getNameAndTypeInfo().getType().equals(makeIndyType())) {
   260                         throw new
   240                         fail("type mismatch for CONSTANT_InvokeDynamic_info " +
   261                             AssertionError("type mismatch for CONSTANT_InvokeDynamic_info " + source.source + "\n" + indyInfo.getNameAndTypeInfo().getType() + "\n" + makeIndyType(id));
   241                                 res.compilationInfo() + "\n" + indyInfo.getNameAndTypeInfo().getType() +
       
   242                                 "\n" + makeIndyType());
       
   243                         return;
   262                     }
   244                     }
   263                 }
   245                 }
   264             }
   246             }
   265             if (bsmIdx == -1) {
   247             if (bsmIdx == -1) {
   266                 throw new Error("Missing invokedynamic in generated code");
   248                 fail("Missing invokedynamic in generated code");
       
   249                 return;
   267             }
   250             }
   268 
   251 
   269             BootstrapMethods_attribute bsm_attr =
   252             BootstrapMethods_attribute bsm_attr =
   270                     (BootstrapMethods_attribute)cf
   253                     (BootstrapMethods_attribute)cf
   271                     .getAttribute(Attribute.BootstrapMethods);
   254                     .getAttribute(Attribute.BootstrapMethods);
   272             if (bsm_attr.bootstrap_method_specifiers.length != 1) {
   255             if (bsm_attr.bootstrap_method_specifiers.length != 1) {
   273                 throw new Error("Bad number of method specifiers " +
   256                 fail("Bad number of method specifiers " +
   274                         "in BootstrapMethods attribute");
   257                         "in BootstrapMethods attribute");
       
   258                 return;
   275             }
   259             }
   276             BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec =
   260             BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec =
   277                     bsm_attr.bootstrap_method_specifiers[0];
   261                     bsm_attr.bootstrap_method_specifiers[0];
   278 
   262 
   279             if (bsm_spec.bootstrap_arguments.length != MF_ARITY) {
   263             if (bsm_spec.bootstrap_arguments.length != MF_ARITY) {
   280                 throw new Error("Bad number of static invokedynamic args " +
   264                 fail("Bad number of static invokedynamic args " +
   281                         "in BootstrapMethod attribute");
   265                         "in BootstrapMethod attribute");
       
   266                 return;
   282             }
   267             }
   283 
   268 
   284             CONSTANT_MethodHandle_info mh =
   269             CONSTANT_MethodHandle_info mh =
   285                     (CONSTANT_MethodHandle_info)cf.constant_pool.get(bsm_spec.bootstrap_arguments[1]);
   270                     (CONSTANT_MethodHandle_info)cf.constant_pool.get(bsm_spec.bootstrap_arguments[1]);
   286 
   271 
   292                 default:
   277                 default:
   293                     kindOK = false;
   278                     kindOK = false;
   294             }
   279             }
   295 
   280 
   296             if (!kindOK) {
   281             if (!kindOK) {
   297                 throw new Error("Bad invoke kind in implementation method handle");
   282                 fail("Bad invoke kind in implementation method handle");
       
   283                 return;
   298             }
   284             }
   299 
   285 
   300             if (!mh.getCPRefInfo().getNameAndTypeInfo().getType().toString().equals(MH_SIG)) {
   286             if (!mh.getCPRefInfo().getNameAndTypeInfo().getType().toString().equals(MH_SIG)) {
   301                 throw new Error("Type mismatch in implementation method handle");
   287                 fail("Type mismatch in implementation method handle");
       
   288                 return;
   302             }
   289             }
   303         } catch (Exception e) {
   290         } catch (Exception e) {
   304             e.printStackTrace();
   291             e.printStackTrace();
   305             throw new Error("error reading " + compiledTest +": " + e);
   292             fail("error reading " + res.compilationInfo() + ": " + e);
   306         }
   293         }
   307     }
   294     }
   308     String makeIndyType(int id) {
   295 
       
   296     String makeIndyType() {
   309         StringBuilder buf = new StringBuilder();
   297         StringBuilder buf = new StringBuilder();
   310         buf.append("(");
   298         buf.append("(");
   311         if (!mk2.isStatic()) {
   299         if (!mk2.isStatic()) {
   312             buf.append(String.format("LTest%d;", id));
   300             buf.append("LTest;");
   313         }
   301         }
   314         buf.append(")Ljava/lang/Runnable;");
   302         buf.append(")Ljava/lang/Runnable;");
   315         return buf.toString();
   303         return buf.toString();
   316     }
   304     }
   317 
       
   318     static final int MF_ARITY = 3;
       
   319     static final String MH_SIG = "()V";
       
   320 
       
   321     class JavaSource extends SimpleJavaFileObject {
       
   322 
       
   323         static final String source_template =
       
   324                 "#CK Test#ID {\n" +
       
   325                 "   #MOD1 void test() { #EK }\n" +
       
   326                 "   #MOD2 void target() { }\n" +
       
   327                 "}\n";
       
   328 
       
   329         String source;
       
   330 
       
   331         JavaSource(int id) {
       
   332             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
       
   333             source = source_template.replace("#CK", mk1.ck.classStr)
       
   334                     .replace("#MOD1", mk1.mods())
       
   335                     .replace("#MOD2", mk2.mods())
       
   336                     .replace("#EK", ek.exprString)
       
   337                     .replace("#ID", String.valueOf(id));
       
   338         }
       
   339 
       
   340         @Override
       
   341         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
       
   342             return source;
       
   343         }
       
   344     }
       
   345 
       
   346     static class DiagChecker
       
   347         implements javax.tools.DiagnosticListener<JavaFileObject> {
       
   348 
       
   349         boolean diagFound;
       
   350         ArrayList<String> diags = new ArrayList<>();
       
   351 
       
   352         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
       
   353             diags.add(diagnostic.getMessage(Locale.getDefault()));
       
   354             diagFound = true;
       
   355         }
       
   356 
       
   357         String printDiags() {
       
   358             StringBuilder buf = new StringBuilder();
       
   359             for (String s : diags) {
       
   360                 buf.append(s);
       
   361                 buf.append("\n");
       
   362             }
       
   363             return buf.toString();
       
   364         }
       
   365     }
       
   366 
       
   367 }
   305 }