1/*
2 * Copyright (c) 2016, 2017, 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 8142968 8154956 8170987 8171412
27 * @summary Test --add-modules and --limit-modules; also test the "enabled" modules.
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 *      jdk.compiler/com.sun.tools.javac.model
34 *      jdk.compiler/com.sun.tools.javac.processing
35 *      jdk.compiler/com.sun.tools.javac.util
36 *      jdk.jdeps/com.sun.tools.javap
37 * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask toolbox.JavaTask ModuleTestBase
38 * @run main AddLimitMods
39 */
40
41import java.io.File;
42import java.nio.file.Files;
43import java.nio.file.Path;
44import java.util.AbstractMap.SimpleEntry;
45import java.util.ArrayList;
46import java.util.Arrays;
47import java.util.Collections;
48import java.util.HashSet;
49import java.util.LinkedHashMap;
50import java.util.List;
51import java.util.Map;
52import java.util.Map.Entry;
53import java.util.Objects;
54import java.util.Set;
55
56import javax.annotation.processing.AbstractProcessor;
57import javax.annotation.processing.RoundEnvironment;
58import javax.annotation.processing.SupportedAnnotationTypes;
59import javax.annotation.processing.SupportedOptions;
60import javax.lang.model.SourceVersion;
61import javax.lang.model.element.ModuleElement;
62import javax.lang.model.element.TypeElement;
63
64import com.sun.tools.javac.code.Symbol.ClassSymbol;
65import com.sun.tools.javac.code.Symtab;
66import com.sun.tools.javac.model.JavacElements;
67import com.sun.tools.javac.processing.JavacProcessingEnvironment;
68import com.sun.tools.javac.util.Context;
69
70import toolbox.JarTask;
71import toolbox.JavacTask;
72import toolbox.JavaTask;
73import toolbox.Task;
74
75public class AddLimitMods extends ModuleTestBase {
76
77    public static void main(String... args) throws Exception {
78        AddLimitMods t = new AddLimitMods();
79        t.runTests();
80    }
81
82    @Test
83    public void testManual(Path base) throws Exception {
84        Path moduleSrc = base.resolve("module-src");
85        Path m1 = moduleSrc.resolve("m1x");
86
87        tb.writeJavaFiles(m1,
88                          "module m1x { requires m2x; requires m3x; }");
89
90        Path m2 = moduleSrc.resolve("m2x");
91
92        tb.writeJavaFiles(m2,
93                          "module m2x { requires m3x; exports m2x; }",
94                          "package m2x; public class M2 {}");
95
96        Path m3 = moduleSrc.resolve("m3x");
97
98        tb.writeJavaFiles(m3,
99                          "module m3x { exports m3x; }",
100                          "package m3x; public class M3 {}");
101
102        Path modulePath = base.resolve("module-path");
103
104        Files.createDirectories(modulePath);
105
106        new JavacTask(tb)
107                .options("--module-source-path", moduleSrc.toString())
108                .outdir(modulePath)
109                .files(findJavaFiles(m3))
110                .run()
111                .writeAll();
112
113        new JavacTask(tb)
114                .options("--module-source-path", moduleSrc.toString())
115                .outdir(modulePath)
116                .files(findJavaFiles(m2))
117                .run()
118                .writeAll();
119
120        //real test
121        new JavacTask(tb)
122                .options("--module-path", modulePath.toString(),
123                         "--should-stop:ifNoError=FLOW",
124                         "--limit-modules", "java.base")
125                .outdir(modulePath)
126                .files(findJavaFiles(m1))
127                .run(Task.Expect.FAIL)
128                .writeAll();
129
130        new JavacTask(tb)
131                .options("--module-path", modulePath.toString(),
132                         "--should-stop:ifNoError=FLOW",
133                         "--limit-modules", "java.base",
134                         "--add-modules", "m2x")
135                .outdir(modulePath)
136                .files(findJavaFiles(m1))
137                .run(Task.Expect.FAIL)
138                .writeAll();
139
140        new JavacTask(tb)
141                .options("--module-path", modulePath.toString(),
142                         "--should-stop:ifNoError=FLOW",
143                         "--limit-modules", "java.base",
144                         "--add-modules", "m2x,m3x")
145                .outdir(modulePath)
146                .files(findJavaFiles(m1))
147                .run()
148                .writeAll();
149
150        new JavacTask(tb)
151                .options("--module-path", modulePath.toString(),
152                         "--should-stop:ifNoError=FLOW",
153                         "--limit-modules", "m2x")
154                .outdir(modulePath)
155                .files(findJavaFiles(m1))
156                .run()
157                .writeAll();
158
159        new JavacTask(tb)
160                .options("--module-path", modulePath.toString(),
161                         "--should-stop:ifNoError=FLOW",
162                         "--limit-modules", "m3x")
163                .outdir(modulePath)
164                .files(findJavaFiles(m1))
165                .run(Task.Expect.FAIL)
166                .writeAll();
167
168        new JavacTask(tb)
169                .options("--module-path", modulePath.toString(),
170                         "--should-stop:ifNoError=FLOW",
171                         "--limit-modules", "m3x",
172                         "--add-modules", "m2x")
173                .outdir(modulePath)
174                .files(findJavaFiles(m1))
175                .run()
176                .writeAll();
177    }
178
179    @Test
180    public void testObservableForUnnamed(Path base) throws Exception {
181        Path src = base.resolve("src");
182
183        tb.writeJavaFiles(src,
184                          "package test;\n" +
185                          "@javax.annotation.Generated(\"test\")\n" +
186                          "public class Test {\n" +
187                          "    com.sun.tools.javac.Main m;\n" +
188                          "    javax.xml.bind.JAXBException e;\n" +
189                          "}\n");
190
191        Path out = base.resolve("out");
192
193        Files.createDirectories(out);
194
195        for (Entry<String[], String> variant : variants) {
196            System.err.println("running variant: options=" + Arrays.asList(variant.getKey()) + ", expected log: " + variant.getValue());
197
198            List<String> options = new ArrayList<>();
199            options.add("-XDrawDiagnostics");
200            options.addAll(Arrays.asList(variant.getKey()));
201
202            String log = new JavacTask(tb)
203                    .options(options.toArray(new String[0]))
204                    .outdir(out)
205                    .files(findJavaFiles(src))
206                    .run(variant.getValue() == null ? Task.Expect.SUCCESS : Task.Expect.FAIL)
207                    .writeAll()
208                    .getOutput(Task.OutputKind.DIRECT);
209
210            log = log.replace(System.getProperty("line.separator"), "\n");
211
212            if (variant.getValue() != null && !log.equals(variant.getValue())) {
213                throw new AssertionError();
214            }
215        }
216    }
217
218    private static final List<Entry<String[], String>> variants = Arrays.asList(
219            new SimpleEntry<String[], String>(new String[] {},
220                                              "Test.java:2:7: compiler.err.package.not.visible: javax.annotation, (compiler.misc.not.def.access.does.not.read.from.unnamed: javax.annotation, java.xml.ws.annotation)\n"
221                                            + "Test.java:5:14: compiler.err.package.not.visible: javax.xml.bind, (compiler.misc.not.def.access.does.not.read.from.unnamed: javax.xml.bind, java.xml.bind)\n"
222                                            + "2 errors\n"),
223            new SimpleEntry<String[], String>(new String[] {"--add-modules", "java.xml.ws.annotation,java.xml.bind"},
224                                              null),
225            new SimpleEntry<String[], String>(new String[] {"--limit-modules", "java.xml.ws,jdk.compiler"},
226                                              null),
227            new SimpleEntry<String[], String>(new String[] {"--add-modules", "ALL-SYSTEM"},
228                                              null)
229    );
230
231    @Test
232    public void testAllModulePath(Path base) throws Exception {
233        if (Files.isDirectory(base))
234            tb.cleanDirectory(base);
235
236        Path moduleSrc = base.resolve("module-src");
237        Path m1 = moduleSrc.resolve("m1x");
238
239        tb.writeJavaFiles(m1,
240                          "module m1x { exports api; }",
241                          "package api; public class Api { }");
242
243        Path modulePath = base.resolve("module-path");
244
245        Files.createDirectories(modulePath);
246
247        new JavacTask(tb)
248                .options("--module-source-path", moduleSrc.toString())
249                .outdir(modulePath)
250                .files(findJavaFiles(moduleSrc))
251                .run()
252                .writeAll();
253
254        Path cpSrc = base.resolve("cp-src");
255        tb.writeJavaFiles(cpSrc, "package test; public class Test { api.Api api; }");
256
257        Path cpOut = base.resolve("cp-out");
258
259        Files.createDirectories(cpOut);
260
261        new JavacTask(tb)
262                .options("--module-path", modulePath.toString())
263                .outdir(cpOut)
264                .files(findJavaFiles(cpSrc))
265                .run(Task.Expect.FAIL)
266                .writeAll();
267
268        new JavacTask(tb)
269                .options("--module-path", modulePath.toString(),
270                         "--add-modules", "ALL-MODULE-PATH")
271                .outdir(cpOut)
272                .files(findJavaFiles(cpSrc))
273                .run()
274                .writeAll();
275
276        List<String> actual;
277        List<String> expected = Arrays.asList(
278                "- compiler.err.addmods.all.module.path.invalid",
279                "1 error");
280
281        actual = new JavacTask(tb)
282                   .options("--module-source-path", moduleSrc.toString(),
283                            "-XDrawDiagnostics",
284                            "--add-modules", "ALL-MODULE-PATH")
285                   .outdir(modulePath)
286                   .files(findJavaFiles(moduleSrc))
287                   .run(Task.Expect.FAIL)
288                   .writeAll()
289                   .getOutputLines(Task.OutputKind.DIRECT);
290
291        if (!Objects.equals(actual, expected)) {
292            throw new IllegalStateException("incorrect errors; actual=" + actual + "; expected=" + expected);
293        }
294
295        actual = new JavacTask(tb)
296                   .options("--patch-module", "java.base=" + cpSrc.toString(),
297                            "-XDrawDiagnostics",
298                            "--add-modules", "ALL-MODULE-PATH")
299                   .outdir(cpOut)
300                   .files(findJavaFiles(cpSrc))
301                   .run(Task.Expect.FAIL)
302                   .writeAll()
303                   .getOutputLines(Task.OutputKind.DIRECT);
304
305        if (!Objects.equals(actual, expected)) {
306            throw new IllegalStateException("incorrect errors; actual=" + actual + "; expected=" + expected);
307        }
308
309        actual = new JavacTask(tb, Task.Mode.CMDLINE)
310                   .options("-source", "8", "-target", "8",
311                            "-XDrawDiagnostics",
312                            "--add-modules", "ALL-MODULE-PATH")
313                   .outdir(cpOut)
314                   .files(findJavaFiles(cpSrc))
315                   .run(Task.Expect.FAIL)
316                   .writeAll()
317                   .getOutputLines(Task.OutputKind.DIRECT);
318
319        if (!actual.contains("javac: option --add-modules not allowed with target 1.8")) {
320            throw new IllegalStateException("incorrect errors; actual=" + actual);
321        }
322
323        tb.writeJavaFiles(cpSrc, "module m1x {}");
324
325        actual = new JavacTask(tb)
326                   .options("-XDrawDiagnostics",
327                            "--add-modules", "ALL-MODULE-PATH")
328                   .outdir(cpOut)
329                   .files(findJavaFiles(cpSrc))
330                   .run(Task.Expect.FAIL)
331                   .writeAll()
332                   .getOutputLines(Task.OutputKind.DIRECT);
333
334        if (!Objects.equals(actual, expected)) {
335            throw new IllegalStateException("incorrect errors; actual=" + actual + "; expected=" + expected);
336        }
337    }
338
339    @Test
340    public void testRuntime2Compile(Path base) throws Exception {
341        Path classpathSrc = base.resolve("classpath-src");
342        Path classpathOut = base.resolve("classpath-out");
343
344        tb.writeJavaFiles(classpathSrc,
345                          generateCheckAccessibleClass("cp.CP"));
346
347        Files.createDirectories(classpathOut);
348
349        System.err.println("Compiling classpath-src files:");
350        new JavacTask(tb)
351                .outdir(classpathOut)
352                .files(findJavaFiles(classpathSrc))
353                .run()
354                .writeAll()
355                .getOutput(Task.OutputKind.DIRECT);
356
357        Path automaticSrc = base.resolve("automatic-src");
358        Path automaticOut = base.resolve("automatic-out");
359
360        tb.writeJavaFiles(automaticSrc,
361                          generateCheckAccessibleClass("automatic.Automatic"));
362
363        Files.createDirectories(automaticOut);
364
365        System.err.println("Compiling automatic-src files:");
366        new JavacTask(tb)
367                .outdir(automaticOut)
368                .files(findJavaFiles(automaticSrc))
369                .run()
370                .writeAll()
371                .getOutput(Task.OutputKind.DIRECT);
372
373        Path modulePath = base.resolve("module-path");
374
375        Files.createDirectories(modulePath);
376
377        Path automaticJar = modulePath.resolve("automatic.jar");
378
379        System.err.println("Creating automatic.jar:");
380        new JarTask(tb, automaticJar)
381          .baseDir(automaticOut)
382          .files("automatic/Automatic.class")
383          .run();
384
385        Path moduleSrc = base.resolve("module-src");
386        Path m1 = moduleSrc.resolve("m1x");
387
388        tb.writeJavaFiles(m1,
389                          "module m1x { exports api; }",
390                          "package api; public class Api { public void test() { } }");
391
392        System.err.println("Compiling module-src files:");
393        new JavacTask(tb)
394                .options("--module-source-path", moduleSrc.toString())
395                .outdir(modulePath)
396                .files(findJavaFiles(moduleSrc))
397                .run()
398                .writeAll()
399                .getOutput(Task.OutputKind.DIRECT);
400
401        int index = 0;
402
403        for (String moduleInfo : MODULE_INFO_VARIANTS) {
404            for (String[] options : OPTIONS_VARIANTS) {
405                index++;
406
407                System.err.println("Running check: " + moduleInfo + "; " + Arrays.asList(options));
408
409                Path m2Runtime = base.resolve(index + "-runtime").resolve("m2x");
410                Path out = base.resolve(index + "-runtime").resolve("out").resolve("m2x");
411
412                Files.createDirectories(out);
413
414                StringBuilder testClassNamed = new StringBuilder();
415
416                testClassNamed.append("package test;\n" +
417                                      "public class Test {\n" +
418                                      "    public static void main(String... args) throws Exception {\n");
419
420                for (Entry<String, String> e : MODULES_TO_CHECK_TO_SAMPLE_CLASS.entrySet()) {
421                    testClassNamed.append("        System.err.println(\"visible:" + e.getKey() + ":\" + ModuleLayer.boot().findModule(\"" + e.getKey() + "\").isPresent());\n");
422                }
423
424                testClassNamed.append("        Class<?> cp = Class.forName(Test.class.getClassLoader().getUnnamedModule(), \"cp.CP\");\n");
425                testClassNamed.append("        cp.getDeclaredMethod(\"runMe\").invoke(null);\n");
426
427                testClassNamed.append("        Class<?> automatic = Class.forName(ModuleLayer.boot().findModule(\"automatic\").get(), \"automatic.Automatic\");\n");
428                testClassNamed.append("        automatic.getDeclaredMethod(\"runMe\").invoke(null);\n");
429
430                testClassNamed.append("    }\n" +
431                                      "}");
432
433                tb.writeJavaFiles(m2Runtime, moduleInfo, testClassNamed.toString());
434
435                System.err.println("Compiling " + m2Runtime + " files:");
436                new JavacTask(tb)
437                   .options("--module-path", modulePath.toString())
438                   .outdir(out)
439                   .files(findJavaFiles(m2Runtime))
440                   .run()
441                   .writeAll();
442
443                boolean success;
444                String output;
445
446                try {
447                    System.err.println("Running m2x/test.Test:");
448                    output = new JavaTask(tb)
449                       .vmOptions(augmentOptions(options,
450                                                 Collections.emptyList(),
451                                                 "--module-path", modulePath.toString() + File.pathSeparator + out.getParent().toString(),
452                                                 "--class-path", classpathOut.toString(),
453                                                 "--add-reads", "m2x=ALL-UNNAMED,automatic",
454                                                 "-m", "m2x/test.Test"))
455                       .run()
456                       .writeAll()
457                       .getOutput(Task.OutputKind.STDERR);
458
459                    success = true;
460                } catch (Task.TaskError err) {
461                    success = false;
462                    output = "";
463                }
464
465                Path m2 = base.resolve(String.valueOf(index)).resolve("m2x");
466
467                tb.writeJavaFiles(m2,
468                                  moduleInfo,
469                                  "package test;\n" +
470                                  "public class Test {}\n");
471
472                List<String> auxOptions = success ? Arrays.asList(
473                    "--processor-path", System.getProperty("test.class.path"),
474                    "-processor", CheckVisibleModule.class.getName(),
475                    "-Aoutput=" + output,
476                    "-XDaccessInternalAPI=true"
477                ) : Collections.emptyList();
478
479                System.err.println("Compiling/processing m2x files:");
480                new JavacTask(tb)
481                   .options(augmentOptions(options,
482                                           auxOptions,
483                                           "--module-path", modulePath.toString(),
484                                           "--class-path", classpathOut.toString(),
485                                           "--should-stop:ifNoError=FLOW"))
486                   .outdir(modulePath)
487                   .files(findJavaFiles(m2))
488                   .run(success ? Task.Expect.SUCCESS : Task.Expect.FAIL)
489                   .writeAll();
490            }
491        }
492    }
493
494    private String generateCheckAccessibleClass(String fqn) {
495        String packageName = fqn.substring(0, fqn.lastIndexOf('.'));
496        String simpleName = fqn.substring(fqn.lastIndexOf('.') + 1);
497        StringBuilder checkClassesAccessible = new StringBuilder();
498        checkClassesAccessible.append("package " + packageName + ";" +
499                                      "public class " + simpleName + " {" +
500                                      "    public static void runMe() throws Exception {");
501        for (Entry<String, String> e : MODULES_TO_CHECK_TO_SAMPLE_CLASS.entrySet()) {
502            checkClassesAccessible.append("try {");
503            checkClassesAccessible.append("Class.forName(\"" + e.getValue() + "\").newInstance();");
504            checkClassesAccessible.append("System.err.println(\"" + fqn + ":" + e.getKey() + ":true\");");
505            checkClassesAccessible.append("} catch (Exception ex) {");
506            checkClassesAccessible.append("System.err.println(\"" + fqn + ":" + e.getKey() + ":false\");");
507            checkClassesAccessible.append("}");
508        }
509
510        checkClassesAccessible.append("    }\n" +
511                                      "}");
512
513        return checkClassesAccessible.toString();
514    }
515
516    private static final Map<String, String> MODULES_TO_CHECK_TO_SAMPLE_CLASS = new LinkedHashMap<>();
517
518    static {
519        MODULES_TO_CHECK_TO_SAMPLE_CLASS.put("m1x", "api.Api");
520        MODULES_TO_CHECK_TO_SAMPLE_CLASS.put("m2x", "test.Test");
521        MODULES_TO_CHECK_TO_SAMPLE_CLASS.put("java.base", "java.lang.Object");
522    };
523
524    @SupportedAnnotationTypes("*")
525    @SupportedOptions("output")
526    public static final class CheckVisibleModule extends AbstractProcessor {
527
528        @Override
529        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
530            String expected = processingEnv.getOptions().get("output");
531            Set<String> expectedElements = new HashSet<>(Arrays.asList(expected.split(System.getProperty("line.separator"))));
532            Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
533            Symtab syms = Symtab.instance(context);
534
535            for (Entry<String, String> e : MODULES_TO_CHECK_TO_SAMPLE_CLASS.entrySet()) {
536                String module = e.getKey();
537                ModuleElement mod = processingEnv.getElementUtils().getModuleElement(module);
538                String visible = "visible:" + module + ":" + (mod != null);
539
540                if (!expectedElements.contains(visible)) {
541                    throw new AssertionError("actual: " + visible + "; expected: " + expected);
542                }
543
544                JavacElements javacElements = JavacElements.instance(context);
545                ClassSymbol unnamedClass = javacElements.getTypeElement(syms.unnamedModule, e.getValue());
546                String unnamed = "cp.CP:" + module + ":" + (unnamedClass != null);
547
548                if (!expectedElements.contains(unnamed)) {
549                    throw new AssertionError("actual: " + unnamed + "; expected: " + expected);
550                }
551
552                ModuleElement automaticMod = processingEnv.getElementUtils().getModuleElement("automatic");
553                ClassSymbol automaticClass = javacElements.getTypeElement(automaticMod, e.getValue());
554                String automatic = "automatic.Automatic:" + module + ":" + (automaticClass != null);
555
556                if (!expectedElements.contains(automatic)) {
557                    throw new AssertionError("actual: " + automatic + "; expected: " + expected);
558                }
559            }
560
561            return false;
562        }
563
564        @Override
565        public SourceVersion getSupportedSourceVersion() {
566            return SourceVersion.latest();
567        }
568
569    }
570
571    public String[] augmentOptions(String[] options, List<String> auxOptions, String... baseOptions) {
572        List<String> all = new ArrayList<>();
573
574        all.addAll(Arrays.asList(options));
575        all.addAll(Arrays.asList(baseOptions));
576        all.addAll(auxOptions);
577
578        return all.toArray(new String[0]);
579    }
580
581    private static final String[] MODULE_INFO_VARIANTS = {
582        "module m2x { exports test; }",
583        "module m2x { requires m1x; exports test; }"
584    };
585
586    private static final String[][] OPTIONS_VARIANTS = {
587        {"--add-modules", "automatic"},
588        {"--add-modules", "m1x,automatic"},
589        {"--add-modules", "jdk.compiler,automatic"},
590        {"--add-modules", "m1x,jdk.compiler,automatic"},
591        {"--add-modules", "ALL-SYSTEM,automatic"},
592        {"--limit-modules", "java.base", "--add-modules", "automatic"},
593        {"--limit-modules", "java.base", "--add-modules", "ALL-SYSTEM,automatic"},
594        {"--limit-modules", "m2x", "--add-modules", "automatic"},
595        {"--limit-modules", "jdk.compiler", "--add-modules", "automatic"},
596    };
597}
598