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
  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 
  37 import java.io.Writer;
  38 import java.nio.file.Files;
  39 import java.nio.file.Path;
  40 import java.nio.file.Paths;
  41 import java.util.Arrays;
  42 import java.util.HashSet;
  43 import java.util.List;
  44 import java.util.Objects;
  45 import java.util.Set;
  46 
  47 import javax.lang.model.element.Element;
  48 import javax.tools.JavaCompiler;
  49 import javax.tools.JavaFileObject;
  50 import javax.tools.StandardJavaFileManager;
  51 import javax.tools.ToolProvider;
  52 
  53 import com.sun.source.tree.CompilationUnitTree;
  54 //import com.sun.source.util.JavacTask; // conflicts with toolbox.JavacTask
  55 import com.sun.tools.javac.api.JavacTaskImpl;
  56 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
  57 
  58 import toolbox.JarTask;
  59 import toolbox.JavacTask;
  60 import toolbox.Task;
  61 
  62 public class EdgeCases extends ModuleTestBase {
  63 
  64     public static void main(String... args) throws Exception {
  65         new EdgeCases().runTests();
  66     }
  67 
  68     @Test
  69     public void testAddExportUndefinedModule(Path base) throws Exception {
  70         Path src = base.resolve("src");
  71         tb.writeJavaFiles(src, "package test; import undef.Any; public class Test {}");
  72         Path classes = base.resolve("classes");
  73         tb.createDirectories(classes);
  74 
  75         List<String> log = new JavacTask(tb)
  76                 .options("--add-exports", "undef/undef=ALL-UNNAMED", "-XDrawDiagnostics")
  77                 .outdir(classes)
  78                 .files(findJavaFiles(src))
  79                 .run(Task.Expect.FAIL)
  80                 .writeAll()
  81                 .getOutputLines(Task.OutputKind.DIRECT);
  82 
  83         List<String> expected = Arrays.asList("- compiler.err.cant.find.module: undef",
  84                                               "Test.java:1:27: compiler.err.doesnt.exist: undef",
  85                                               "2 errors");
  86 
  87         if (!expected.equals(log))
  88             throw new Exception("expected output not found: " + log);
  89     }
  90 
  91     @Test
  92     public void testModuleSymbolOutterMostClass(Path base) throws Exception {
  93         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  94         try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
  95             Path moduleSrc = base.resolve("module-src");
  96             Path m1 = moduleSrc.resolve("m1");
  97 
  98             tb.writeJavaFiles(m1, "module m1 { }");
  99 
 100             Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(findJavaFiles(moduleSrc));
 101             com.sun.source.util.JavacTask task =
 102                 (com.sun.source.util.JavacTask) compiler.getTask(null, fm, null, null, null, files);
 103 
 104             task.analyze();
 105 
 106             ModuleSymbol msym = (ModuleSymbol) task.getElements().getModuleElement("m1");
 107 
 108             msym.outermostClass();
 109         }
 110     }
 111 
 112     @Test
 113     public void testParseEnterAnalyze(Path base) throws Exception {
 114         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 115         try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
 116             Path moduleSrc = base.resolve("module-src");
 117             Path m1 = moduleSrc.resolve("m1");
 118 
 119             tb.writeJavaFiles(m1, "module m1 { }",
 120                                   "package p;",
 121                                   "package p; class T { }");
 122 
 123             Path classes = base.resolve("classes");
 124             Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(findJavaFiles(moduleSrc));
 125             List<String> options = Arrays.asList("-d", classes.toString(), "-Xpkginfo:always");
 126             JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, fm, null, options, null, files);
 127 
 128             Iterable<? extends CompilationUnitTree> parsed = task.parse();
 129             Iterable<? extends Element> entered = task.enter(parsed);
 130             Iterable<? extends Element> analyzed = task.analyze(entered);
 131             Iterable<? extends JavaFileObject> generatedFiles = task.generate(analyzed);
 132 
 133             Set<String> generated = new HashSet<>();
 134 
 135             for (JavaFileObject jfo : generatedFiles) {
 136                 generated.add(jfo.getName());
 137             }
 138 
 139             Set<String> expected = new HashSet<>(
 140                     Arrays.asList(Paths.get("testParseEnterAnalyze", "classes", "p", "package-info.class").toString(),
 141                                   Paths.get("testParseEnterAnalyze", "classes", "module-info.class").toString(),
 142                                   Paths.get("testParseEnterAnalyze", "classes", "p", "T.class").toString())
 143             );
 144 
 145             if (!Objects.equals(expected, generated))
 146                 throw new AssertionError("Incorrect generated files: " + generated);
 147         }
 148     }
 149 
 150     @Test
 151     public void testModuleImplicitModuleBoundaries(Path base) throws Exception {
 152         Path src = base.resolve("src");
 153         Path src_m1 = src.resolve("m1");
 154         tb.writeJavaFiles(src_m1,
 155                           "module m1 { exports api1; }",
 156                           "package api1; public class Api1 { public void call() { } }");
 157         Path src_m2 = src.resolve("m2");
 158         tb.writeJavaFiles(src_m2,
 159                           "module m2 { requires m1; exports api2; }",
 160                           "package api2; public class Api2 { public static api1.Api1 get() { return null; } }");
 161         Path src_m3 = src.resolve("m3");
 162         tb.writeJavaFiles(src_m3,
 163                           "module m3 { requires m2; }",
 164                           "package test; public class Test { { api2.Api2.get().call(); api2.Api2.get().toString(); } }");
 165         Path classes = base.resolve("classes");
 166         tb.createDirectories(classes);
 167 
 168         String log = new JavacTask(tb)
 169                 .options("-XDrawDiagnostics",
 170                          "--module-source-path", src.toString())
 171                 .outdir(classes)
 172                 .files(findJavaFiles(src))
 173                 .run(Task.Expect.FAIL)
 174                 .writeAll()
 175                 .getOutput(Task.OutputKind.DIRECT);
 176 
 177         if (!log.contains("Test.java:1:52: compiler.err.not.def.access.class.intf.cant.access: call(), api1.Api1") ||
 178             !log.contains("Test.java:1:76: compiler.err.not.def.access.class.intf.cant.access: toString(), java.lang.Object"))
 179             throw new Exception("expected output not found");
 180     }
 181 
 182     @Test
 183     public void testAssignClassToAutomaticModule(Path base) throws Exception {
 184         //check that if a ClassSymbol belongs to an automatic module, it is properly assigned and not
 185         //duplicated when being accessed through a classfile.
 186         Path automaticSrc = base.resolve("automaticSrc");
 187         tb.writeJavaFiles(automaticSrc, "package api1; public class Api1 {}");
 188         Path automaticClasses = base.resolve("automaticClasses");
 189         tb.createDirectories(automaticClasses);
 190 
 191         String automaticLog = new JavacTask(tb)
 192                                 .outdir(automaticClasses)
 193                                 .files(findJavaFiles(automaticSrc))
 194                                 .run()
 195                                 .writeAll()
 196                                 .getOutput(Task.OutputKind.DIRECT);
 197 
 198         if (!automaticLog.isEmpty())
 199             throw new Exception("expected output not found: " + automaticLog);
 200 
 201         Path modulePath = base.resolve("module-path");
 202 
 203         Files.createDirectories(modulePath);
 204 
 205         Path automaticJar = modulePath.resolve("m1-1.0.jar");
 206 
 207         new JarTask(tb, automaticJar)
 208           .baseDir(automaticClasses)
 209           .files("api1/Api1.class")
 210           .run();
 211 
 212         Path src = base.resolve("src");
 213         Path src_m2 = src.resolve("m2");
 214         tb.writeJavaFiles(src_m2,
 215                           "module m2 { requires m1; exports api2; }",
 216                           "package api2; public class Api2 { public static api1.Api1 get() { return null; } }");
 217         Path src_m3 = src.resolve("m3");
 218         tb.writeJavaFiles(src_m3,
 219                           "module m3 { requires m1; requires m2; }",
 220                           "package test; public class Test { { api2.Api2.get(); api1.Api1 a1; } }");
 221         Path classes = base.resolve("classes");
 222         tb.createDirectories(classes);
 223 
 224         new JavacTask(tb)
 225                 .options("--module-path", modulePath.toString(),
 226                          "--module-source-path", src.toString())
 227                 .outdir(classes)
 228                 .files(findJavaFiles(src_m2))
 229                 .run()
 230                 .writeAll();
 231 
 232         new JavacTask(tb)
 233                 .options("--module-path", modulePath.toString(),
 234                          "--module-source-path", src.toString())
 235                 .outdir(classes)
 236                 .files(findJavaFiles(src_m3))
 237                 .run()
 238                 .writeAll();
 239     }
 240 
 241     @Test
 242     public void testEmptyImplicitModuleInfo(Path base) throws Exception {
 243         Path src = base.resolve("src");
 244         Path src_m1 = src.resolve("m1");
 245         Files.createDirectories(src_m1);
 246         try (Writer w = Files.newBufferedWriter(src_m1.resolve("module-info.java"))) {}
 247         tb.writeJavaFiles(src_m1,
 248                           "package test; public class Test {}");
 249         Path classes = base.resolve("classes");
 250         tb.createDirectories(classes);
 251 
 252         new JavacTask(tb)
 253                 .options("--source-path", src_m1.toString(),
 254                          "-XDrawDiagnostics")
 255                 .outdir(classes)
 256                 .files(findJavaFiles(src_m1.resolve("test")))
 257                 .run(Task.Expect.FAIL)
 258                 .writeAll();
 259 
 260         tb.writeJavaFiles(src_m1,
 261                           "module m1 {}");
 262 
 263         new JavacTask(tb)
 264                 .options("--source-path", src_m1.toString())
 265                 .outdir(classes)
 266                 .files(findJavaFiles(src_m1.resolve("test")))
 267                 .run()
 268                 .writeAll();
 269 
 270     }
 271 
 272     @Test
 273     public void testClassPackageClash(Path base) throws Exception {
 274         Path src = base.resolve("src");
 275         Path src_m1 = src.resolve("m1");
 276         tb.writeJavaFiles(src_m1,
 277                           "module m1 { exports test.m1; }",
 278                           "package test.m1;\n" +
 279                           "public class Test {}\n");
 280         Path src_m2 = src.resolve("m2");
 281         tb.writeJavaFiles(src_m2,
 282                           "module m2 { requires m1; }",
 283                           "package test;\n" +
 284                           "public class m1 {}\n");
 285         Path classes = base.resolve("classes");
 286         tb.createDirectories(classes);
 287 
 288         List<String> log = new JavacTask(tb)
 289                 .options("--module-source-path", src.toString(),
 290                          "-XDrawDiagnostics")
 291                 .outdir(classes)
 292                 .files(findJavaFiles(src))
 293                 .run(Task.Expect.FAIL)
 294                 .writeAll()
 295                 .getOutputLines(Task.OutputKind.DIRECT);
 296 
 297         List<String> expected = Arrays.asList(
 298             "m1.java:2:8: compiler.err.clash.with.pkg.of.same.name: kindname.class, test.m1",
 299             "1 error"
 300         );
 301 
 302         if (!expected.equals(log)) {
 303             throw new IllegalStateException(log.toString());
 304         }
 305     }
 306 
 307 }