EdgeCases.java revision 3740:d3dde3f775b8
1/*
2 * Copyright (c) 2015, 2016, 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 */
23
24/*
25 * @test
26 * @bug 8154283 8167320
27 * @summary tests for multi-module mode compilation
28 * @library /tools/lib
29 * @modules
30 *      jdk.compiler/com.sun.tools.javac.api
31 *      jdk.compiler/com.sun.tools.javac.code
32 *      jdk.compiler/com.sun.tools.javac.main
33 * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask ModuleTestBase
34 * @run main EdgeCases
35 */
36
37import java.io.Writer;
38import java.nio.file.Files;
39import java.nio.file.Path;
40import java.nio.file.Paths;
41import java.util.Arrays;
42import java.util.HashSet;
43import java.util.List;
44import java.util.Objects;
45import java.util.Set;
46
47import javax.lang.model.element.Element;
48import javax.tools.JavaCompiler;
49import javax.tools.JavaFileObject;
50import javax.tools.StandardJavaFileManager;
51import javax.tools.ToolProvider;
52
53import com.sun.source.tree.CompilationUnitTree;
54//import com.sun.source.util.JavacTask; // conflicts with toolbox.JavacTask
55import com.sun.tools.javac.api.JavacTaskImpl;
56import com.sun.tools.javac.code.Symbol.ModuleSymbol;
57import com.sun.tools.javac.code.Symtab;
58
59import toolbox.JarTask;
60import toolbox.JavacTask;
61import toolbox.Task;
62import toolbox.Task.Expect;
63import toolbox.Task.OutputKind;
64
65public class EdgeCases extends ModuleTestBase {
66
67    public static void main(String... args) throws Exception {
68        new EdgeCases().runTests();
69    }
70
71    @Test
72    public void testAddExportUndefinedModule(Path base) throws Exception {
73        Path src = base.resolve("src");
74        tb.writeJavaFiles(src, "package test; import undef.Any; public class Test {}");
75        Path classes = base.resolve("classes");
76        tb.createDirectories(classes);
77
78        List<String> log = new JavacTask(tb)
79                .options("--add-exports", "undef/undef=ALL-UNNAMED", "-XDrawDiagnostics")
80                .outdir(classes)
81                .files(findJavaFiles(src))
82                .run(Task.Expect.FAIL)
83                .writeAll()
84                .getOutputLines(Task.OutputKind.DIRECT);
85
86        List<String> expected = Arrays.asList("- compiler.err.cant.find.module: undef",
87                                              "Test.java:1:27: compiler.err.doesnt.exist: undef",
88                                              "2 errors");
89
90        if (!expected.equals(log))
91            throw new Exception("expected output not found: " + log);
92    }
93
94    @Test
95    public void testModuleSymbolOutterMostClass(Path base) throws Exception {
96        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
97        try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
98            Path moduleSrc = base.resolve("module-src");
99            Path m1 = moduleSrc.resolve("m1");
100
101            tb.writeJavaFiles(m1, "module m1 { }");
102
103            Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(findJavaFiles(moduleSrc));
104            com.sun.source.util.JavacTask task =
105                (com.sun.source.util.JavacTask) compiler.getTask(null, fm, null, null, null, files);
106
107            task.analyze();
108
109            ModuleSymbol msym = (ModuleSymbol) task.getElements().getModuleElement("m1");
110
111            msym.outermostClass();
112        }
113    }
114
115    @Test
116    public void testParseEnterAnalyze(Path base) throws Exception {
117        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
118        try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
119            Path moduleSrc = base.resolve("module-src");
120            Path m1 = moduleSrc.resolve("m1");
121
122            tb.writeJavaFiles(m1, "module m1 { }",
123                                  "package p;",
124                                  "package p; class T { }");
125
126            Path classes = base.resolve("classes");
127            Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(findJavaFiles(moduleSrc));
128            List<String> options = Arrays.asList("-d", classes.toString(), "-Xpkginfo:always");
129            JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, fm, null, options, null, files);
130
131            Iterable<? extends CompilationUnitTree> parsed = task.parse();
132            Iterable<? extends Element> entered = task.enter(parsed);
133            Iterable<? extends Element> analyzed = task.analyze(entered);
134            Iterable<? extends JavaFileObject> generatedFiles = task.generate(analyzed);
135
136            Set<String> generated = new HashSet<>();
137
138            for (JavaFileObject jfo : generatedFiles) {
139                generated.add(jfo.getName());
140            }
141
142            Set<String> expected = new HashSet<>(
143                    Arrays.asList(Paths.get("testParseEnterAnalyze", "classes", "p", "package-info.class").toString(),
144                                  Paths.get("testParseEnterAnalyze", "classes", "module-info.class").toString(),
145                                  Paths.get("testParseEnterAnalyze", "classes", "p", "T.class").toString())
146            );
147
148            if (!Objects.equals(expected, generated))
149                throw new AssertionError("Incorrect generated files: " + generated);
150        }
151    }
152
153    @Test
154    public void testModuleImplicitModuleBoundaries(Path base) throws Exception {
155        Path src = base.resolve("src");
156        Path src_m1 = src.resolve("m1");
157        tb.writeJavaFiles(src_m1,
158                          "module m1 { exports api1; }",
159                          "package api1; public class Api1 { public void call() { } }");
160        Path src_m2 = src.resolve("m2");
161        tb.writeJavaFiles(src_m2,
162                          "module m2 { requires m1; exports api2; }",
163                          "package api2; public class Api2 { public static api1.Api1 get() { return null; } }");
164        Path src_m3 = src.resolve("m3");
165        tb.writeJavaFiles(src_m3,
166                          "module m3 { requires m2; }",
167                          "package test; public class Test { { api2.Api2.get().call(); api2.Api2.get().toString(); } }");
168        Path classes = base.resolve("classes");
169        tb.createDirectories(classes);
170
171        String log = new JavacTask(tb)
172                .options("-XDrawDiagnostics",
173                         "--module-source-path", src.toString())
174                .outdir(classes)
175                .files(findJavaFiles(src))
176                .run(Task.Expect.FAIL)
177                .writeAll()
178                .getOutput(Task.OutputKind.DIRECT);
179
180        if (!log.contains("Test.java:1:52: compiler.err.not.def.access.class.intf.cant.access.reason: call(), api1.Api1, api1, (compiler.misc.not.def.access.does.not.read: m3, api1, m1)") ||
181            !log.contains("Test.java:1:76: compiler.err.not.def.access.class.intf.cant.access: toString(), java.lang.Object"))
182            throw new Exception("expected output not found");
183    }
184
185    @Test
186    public void testAssignClassToAutomaticModule(Path base) throws Exception {
187        //check that if a ClassSymbol belongs to an automatic module, it is properly assigned and not
188        //duplicated when being accessed through a classfile.
189        Path automaticSrc = base.resolve("automaticSrc");
190        tb.writeJavaFiles(automaticSrc, "package api1; public class Api1 {}");
191        Path automaticClasses = base.resolve("automaticClasses");
192        tb.createDirectories(automaticClasses);
193
194        String automaticLog = new JavacTask(tb)
195                                .outdir(automaticClasses)
196                                .files(findJavaFiles(automaticSrc))
197                                .run()
198                                .writeAll()
199                                .getOutput(Task.OutputKind.DIRECT);
200
201        if (!automaticLog.isEmpty())
202            throw new Exception("expected output not found: " + automaticLog);
203
204        Path modulePath = base.resolve("module-path");
205
206        Files.createDirectories(modulePath);
207
208        Path automaticJar = modulePath.resolve("m1-1.0.jar");
209
210        new JarTask(tb, automaticJar)
211          .baseDir(automaticClasses)
212          .files("api1/Api1.class")
213          .run();
214
215        Path src = base.resolve("src");
216        Path src_m2 = src.resolve("m2");
217        tb.writeJavaFiles(src_m2,
218                          "module m2 { requires m1; exports api2; }",
219                          "package api2; public class Api2 { public static api1.Api1 get() { return null; } }");
220        Path src_m3 = src.resolve("m3");
221        tb.writeJavaFiles(src_m3,
222                          "module m3 { requires m1; requires m2; }",
223                          "package test; public class Test { { api2.Api2.get(); api1.Api1 a1; } }");
224        Path classes = base.resolve("classes");
225        tb.createDirectories(classes);
226
227        new JavacTask(tb)
228                .options("--module-path", modulePath.toString(),
229                         "--module-source-path", src.toString())
230                .outdir(classes)
231                .files(findJavaFiles(src_m2))
232                .run()
233                .writeAll();
234
235        new JavacTask(tb)
236                .options("--module-path", modulePath.toString(),
237                         "--module-source-path", src.toString())
238                .outdir(classes)
239                .files(findJavaFiles(src_m3))
240                .run()
241                .writeAll();
242    }
243
244    @Test
245    public void testEmptyImplicitModuleInfo(Path base) throws Exception {
246        Path src = base.resolve("src");
247        Path src_m1 = src.resolve("m1");
248        Files.createDirectories(src_m1);
249        try (Writer w = Files.newBufferedWriter(src_m1.resolve("module-info.java"))) {}
250        tb.writeJavaFiles(src_m1,
251                          "package test; public class Test {}");
252        Path classes = base.resolve("classes");
253        tb.createDirectories(classes);
254
255        new JavacTask(tb)
256                .options("--source-path", src_m1.toString(),
257                         "-XDrawDiagnostics")
258                .outdir(classes)
259                .files(findJavaFiles(src_m1.resolve("test")))
260                .run(Task.Expect.FAIL)
261                .writeAll();
262
263        tb.writeJavaFiles(src_m1,
264                          "module m1 {}");
265
266        new JavacTask(tb)
267                .options("--source-path", src_m1.toString())
268                .outdir(classes)
269                .files(findJavaFiles(src_m1.resolve("test")))
270                .run()
271                .writeAll();
272
273    }
274
275    @Test
276    public void testClassPackageClash(Path base) throws Exception {
277        Path src = base.resolve("src");
278        Path src_m1 = src.resolve("m1");
279        tb.writeJavaFiles(src_m1,
280                          "module m1 { exports test.m1; }",
281                          "package test.m1;\n" +
282                          "public class Test {}\n");
283        Path src_m2 = src.resolve("m2");
284        tb.writeJavaFiles(src_m2,
285                          "module m2 { requires m1; }",
286                          "package test;\n" +
287                          "public class m1 {}\n");
288        Path classes = base.resolve("classes");
289        tb.createDirectories(classes);
290
291        List<String> log = new JavacTask(tb)
292                .options("--module-source-path", src.toString(),
293                         "-XDrawDiagnostics")
294                .outdir(classes)
295                .files(findJavaFiles(src))
296                .run(Task.Expect.FAIL)
297                .writeAll()
298                .getOutputLines(Task.OutputKind.DIRECT);
299
300        List<String> expected = Arrays.asList(
301            "m1.java:2:8: compiler.err.clash.with.pkg.of.same.name: kindname.class, test.m1",
302            "1 error"
303        );
304
305        if (!expected.equals(log)) {
306            throw new IllegalStateException(log.toString());
307        }
308    }
309
310    @Test
311    public void testImplicitJavaBase(Path base) throws Exception {
312        Path src = base.resolve("src");
313        Path src_java_base = src.resolve("java.base");
314        Files.createDirectories(src_java_base);
315        tb.writeJavaFiles(src_java_base, "module java.base { exports java.lang; }");
316        tb.writeJavaFiles(src_java_base,
317                          "package java.lang; public class Object {}");
318        Path classes = base.resolve("classes");
319        tb.createDirectories(classes);
320
321        //module-info from source:
322        new JavacTask(tb)
323            .options("-sourcepath", src_java_base.toString())
324            .outdir(classes)
325            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
326            .run()
327            .writeAll();
328
329        //module-info from class:
330        if (!Files.exists(classes.resolve("module-info.class"))) {
331            throw new AssertionError("module-info.class not created!");
332        }
333
334        new JavacTask(tb)
335            .outdir(classes)
336            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
337            .run()
338            .writeAll();
339
340        //broken module-info.class:
341        Files.newOutputStream(classes.resolve("module-info.class")).close();
342
343        List<String> log = new JavacTask(tb)
344            .options("-XDrawDiagnostics")
345            .outdir(classes)
346            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
347            .run(Expect.FAIL)
348            .writeAll()
349            .getOutputLines(OutputKind.DIRECT);
350
351        List<String> expected = Arrays.asList(
352                "- compiler.err.cant.access: <error>.module-info, (compiler.misc.bad.class.file.header: module-info.class, (compiler.misc.illegal.start.of.class.file))",
353                "1 error");
354
355        if (!expected.equals(log)) {
356            throw new AssertionError("Unexpected output: " + log);
357        }
358
359        //broken module-info.java:
360        Files.delete(classes.resolve("module-info.class"));
361
362        try (Writer out = Files.newBufferedWriter(src_java_base.resolve("module-info.java"))) {
363            out.write("class Broken {}");
364        }
365
366        log = new JavacTask(tb)
367            .options("-sourcepath", src_java_base.toString(),
368                                "-XDrawDiagnostics")
369            .outdir(classes)
370            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
371            .run(Expect.FAIL)
372            .writeAll()
373            .getOutputLines(OutputKind.DIRECT);
374
375        expected = Arrays.asList("X");
376
377        if (expected.equals(log)) {
378            throw new AssertionError("Unexpected output: " + log);
379        }
380    }
381
382    @Test
383    public void testModuleInfoNameMismatchSource(Path base) throws Exception {
384        Path src = base.resolve("src");
385        Path m1 = src.resolve("m1");
386        Files.createDirectories(m1);
387        tb.writeJavaFiles(m1, "module other { }",
388                              "package test; public class Test {}");
389        Path classes = base.resolve("classes");
390        tb.createDirectories(classes);
391
392        List<String> log = new JavacTask(tb)
393            .options("--module-source-path", src.toString(),
394                     "-XDrawDiagnostics")
395            .outdir(classes)
396            .files(findJavaFiles(m1.resolve("test").resolve("Test.java")))
397            .run(Expect.FAIL)
398            .writeAll()
399            .getOutputLines(OutputKind.DIRECT);
400
401        List<String> expected = Arrays.asList(
402                "module-info.java:1:1: compiler.err.module.name.mismatch: other, m1",
403                "- compiler.err.cant.access: m1.module-info, (compiler.misc.cant.resolve.modules)",
404                "2 errors");
405
406        if (!expected.equals(log)) {
407            throw new AssertionError("Unexpected output: " + log);
408        }
409    }
410
411    @Test
412    public void testModuleInfoNameMismatchClass(Path base) throws Exception {
413        Path src = base.resolve("src");
414        Files.createDirectories(src);
415        tb.writeJavaFiles(src, "module other { }",
416                               "package test; public class Test {}");
417        Path classes = base.resolve("classes");
418        Path m1Classes = classes.resolve("m1");
419        tb.createDirectories(m1Classes);
420
421        new JavacTask(tb)
422            .outdir(m1Classes)
423            .files(findJavaFiles(src))
424            .run()
425            .writeAll()
426            .getOutputLines(OutputKind.DIRECT);
427
428        Path src2 = base.resolve("src2");
429        Files.createDirectories(src2);
430        tb.writeJavaFiles(src2, "module use { requires m1; }");
431
432        Path classes2 = base.resolve("classes2");
433        tb.createDirectories(classes2);
434
435        List<String> log = new JavacTask(tb)
436            .options("--module-path", classes.toString(),
437                     "-XDrawDiagnostics")
438            .outdir(classes2)
439            .files(findJavaFiles(src2))
440            .run(Expect.FAIL)
441            .writeAll()
442            .getOutputLines(OutputKind.DIRECT);
443
444        List<String> expected = Arrays.asList(
445                "- compiler.err.cant.access: m1.module-info, (compiler.misc.bad.class.file.header: module-info.class, (compiler.misc.module.name.mismatch: other, m1))",
446                "1 error");
447
448        if (!expected.equals(log)) {
449            throw new AssertionError("Unexpected output: " + log);
450        }
451    }
452
453    @Test
454    public void testGetDirectivesComplete(Path base) throws Exception {
455        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
456        JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, null, null, null, null, null);
457        Symtab syms = Symtab.instance(task.getContext());
458
459        syms.java_base.getDirectives();
460    }
461}
462