8181087: Module system implementation refresh (6/2017)
authoralanb
Fri, 16 Jun 2017 09:21:38 -0700
changeset 45682 fc3b228b9e2a
parent 45600 6589d4088eaa
child 45683 6b4c8cffb90e
8181087: Module system implementation refresh (6/2017) Reviewed-by: jjg Contributed-by: alan.bateman@oracle.com, jan.lahoda@oracle.com
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
langtools/src/jdk.jdeps/share/classes/module-info.java
langtools/test/tools/javac/diags/examples/PackageClashFromRequiresInUnnamed/PackageClashFromRequiresInUnnamed.java
langtools/test/tools/javac/diags/examples/PackageClashFromRequiresInUnnamed/modulepath/lib1x/exported/Api1.java
langtools/test/tools/javac/diags/examples/PackageClashFromRequiresInUnnamed/modulepath/lib1x/module-info.java
langtools/test/tools/javac/diags/examples/PackageClashFromRequiresInUnnamed/modulepath/lib2x/exported/Api2.java
langtools/test/tools/javac/diags/examples/PackageClashFromRequiresInUnnamed/modulepath/lib2x/module-info.java
langtools/test/tools/javac/modules/AutomaticModules.java
langtools/test/tools/javac/modules/PackageConflictTest.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java	Thu Jun 15 17:24:13 2017 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java	Fri Jun 16 09:21:38 2017 -0700
@@ -1574,8 +1574,13 @@
                                                             : null;
                     DiagnosticPosition pos = env != null ? env.tree.pos() : null;
                     try {
-                        log.error(pos, Errors.PackageClashFromRequires(msym, packageName,
-                                                                      previousModule, exportsFrom));
+                        if (msym.isUnnamed()) {
+                            log.error(pos, Errors.PackageClashFromRequiresInUnnamed(packageName,
+                                                                                    previousModule, exportsFrom));
+                        } else {
+                            log.error(pos, Errors.PackageClashFromRequires(msym, packageName,
+                                                                           previousModule, exportsFrom));
+                        }
                     } finally {
                         if (env != null)
                             log.useSource(origSource);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java	Thu Jun 15 17:24:13 2017 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java	Fri Jun 16 09:21:38 2017 -0700
@@ -29,6 +29,7 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.UncheckedIOException;
 import java.net.URI;
 import java.net.URL;
@@ -65,6 +66,8 @@
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
 
 import javax.lang.model.SourceVersion;
 import javax.tools.JavaFileManager;
@@ -1341,6 +1344,24 @@
                             String moduleName = readModuleName(moduleInfoClass);
                             return new Pair<>(moduleName, p);
                         }
+                        Path mf = fs.getPath("META-INF/MANIFEST.MF");
+                        if (Files.exists(mf)) {
+                            try (InputStream in = Files.newInputStream(mf)) {
+                                Manifest man = new Manifest(in);
+                                Attributes attrs = man.getMainAttributes();
+                                if (attrs != null) {
+                                    String moduleName = attrs.getValue(new Attributes.Name("Automatic-Module-Name"));
+                                    if (moduleName != null) {
+                                        if (isModuleName(moduleName)) {
+                                            return new Pair<>(moduleName, p);
+                                        } else {
+                                            log.error(Errors.LocnCantGetModuleNameForJar(p));
+                                            return null;
+                                        }
+                                    }
+                                }
+                            }
+                        }
                     } catch (ModuleNameReader.BadClassFile e) {
                         log.error(Errors.LocnBadModuleInfo(p));
                         return null;
@@ -1428,6 +1449,22 @@
             }
         }
 
+        //from jdk.internal.module.Checks:
+        /**
+         * Returns {@code true} if the given name is a legal module name.
+         */
+        private boolean isModuleName(String name) {
+            int next;
+            int off = 0;
+            while ((next = name.indexOf('.', off)) != -1) {
+                String id = name.substring(off, next);
+                if (!SourceVersion.isName(id))
+                    return false;
+                off = next+1;
+            }
+            String last = name.substring(off);
+            return SourceVersion.isName(last);
+        }
     }
 
     private class ModuleSourcePathLocationHandler extends BasicLocationHandler {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Thu Jun 15 17:24:13 2017 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Jun 16 09:21:38 2017 -0700
@@ -2990,6 +2990,10 @@
 compiler.err.package.clash.from.requires=\
     module {0} reads package {1} from both {2} and {3}
 
+# 0: name, 1: symbol, 2: symbol
+compiler.err.package.clash.from.requires.in.unnamed=\
+    the unnamed module reads package {0} from both {1} and {2}
+
 # 0: string
 compiler.err.module.not.found.in.module.source.path=\
     module {0} not found in module source path
--- a/langtools/src/jdk.jdeps/share/classes/module-info.java	Thu Jun 15 17:24:13 2017 +0000
+++ b/langtools/src/jdk.jdeps/share/classes/module-info.java	Fri Jun 16 09:21:38 2017 -0700
@@ -30,11 +30,9 @@
  *  @since 9
  */
 module jdk.jdeps {
-    requires java.base;
     requires java.compiler;
     requires jdk.compiler;
-    exports com.sun.tools.classfile to
-        jdk.jlink;
+    exports com.sun.tools.classfile to jdk.jlink;
 
     provides java.util.spi.ToolProvider with
         com.sun.tools.javap.Main.JavapToolProvider,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/PackageClashFromRequiresInUnnamed/PackageClashFromRequiresInUnnamed.java	Fri Jun 16 09:21:38 2017 -0700
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// key: compiler.err.package.clash.from.requires.in.unnamed
+// options: --add-modules ALL-MODULE-PATH
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/PackageClashFromRequiresInUnnamed/modulepath/lib1x/exported/Api1.java	Fri Jun 16 09:21:38 2017 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package exported;
+
+public class Api1 {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/PackageClashFromRequiresInUnnamed/modulepath/lib1x/module-info.java	Fri Jun 16 09:21:38 2017 -0700
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module lib1x {
+     exports exported;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/PackageClashFromRequiresInUnnamed/modulepath/lib2x/exported/Api2.java	Fri Jun 16 09:21:38 2017 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package exported;
+
+public class Api2 {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/PackageClashFromRequiresInUnnamed/modulepath/lib2x/module-info.java	Fri Jun 16 09:21:38 2017 -0700
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module lib2x {
+     exports exported;
+}
--- a/langtools/test/tools/javac/modules/AutomaticModules.java	Thu Jun 15 17:24:13 2017 +0000
+++ b/langtools/test/tools/javac/modules/AutomaticModules.java	Fri Jun 16 09:21:38 2017 -0700
@@ -34,14 +34,18 @@
  * @run main AutomaticModules
  */
 
+import java.io.File;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 import toolbox.JarTask;
 import toolbox.JavacTask;
 import toolbox.Task;
+import toolbox.Task.Mode;
 
 public class AutomaticModules extends ModuleTestBase {
 
@@ -614,4 +618,204 @@
         }
     }
 
+    @Test
+    public void testAutomaticModuleNameCorrect(Path base) throws Exception {
+        Path modulePath = base.resolve("module-path");
+
+        Files.createDirectories(modulePath);
+
+        Path automaticSrc = base.resolve("automaticSrc");
+        tb.writeJavaFiles(automaticSrc, "package api; public class Api {}");
+        Path automaticClasses = base.resolve("automaticClasses");
+        tb.createDirectories(automaticClasses);
+
+        String automaticLog = new JavacTask(tb)
+                                .outdir(automaticClasses)
+                                .files(findJavaFiles(automaticSrc))
+                                .run()
+                                .writeAll()
+                                .getOutput(Task.OutputKind.DIRECT);
+
+        if (!automaticLog.isEmpty())
+            throw new Exception("expected output not found: " + automaticLog);
+
+        Path automaticJar = modulePath.resolve("automatic-1.0.jar");
+
+        new JarTask(tb, automaticJar)
+          .baseDir(automaticClasses)
+          .files("api/Api.class")
+          .manifest("Automatic-Module-Name: custom.module.name\n\n")
+          .run();
+
+        Path src = base.resolve("src");
+
+        tb.writeJavaFiles(src,
+                          "module m { requires custom.module.name; }",
+                          "package impl; public class Impl { api.Api a; }");
+
+        Path classes = base.resolve("classes");
+
+        Files.createDirectories(classes);
+
+        new JavacTask(tb)
+                .options("--module-path", modulePath.toString(),
+                         "-XDrawDiagnostics")
+                .outdir(classes)
+                .files(findJavaFiles(src))
+                .run(Task.Expect.SUCCESS)
+                .writeAll()
+                .getOutputLines(Task.OutputKind.DIRECT);
+
+        tb.writeJavaFiles(src,
+                          "module m { requires automatic; }");
+
+        List<String> log = new JavacTask(tb)
+                .options("--module-path", modulePath.toString(),
+                         "-XDrawDiagnostics")
+                .outdir(classes)
+                .files(findJavaFiles(src))
+                .run(Task.Expect.FAIL)
+                .writeAll()
+                .getOutputLines(Task.OutputKind.DIRECT);
+
+        List<String> expected =
+                Arrays.asList("module-info.java:1:21: compiler.err.module.not.found: automatic",
+                              "1 error");
+
+        if (!expected.equals(log)) {
+            throw new Exception("expected output not found: " + log);
+        }
+    }
+
+    @Test
+    public void testAutomaticModuleNameIncorrect(Path base) throws Exception {
+        for (String name : new String[] {"", "999", "foo.class", "foo._"}) {
+            if (Files.isDirectory(base)) {
+                tb.cleanDirectory(base);
+            }
+            Path modulePath = base.resolve("module-path");
+
+            Files.createDirectories(modulePath);
+
+            Path automaticSrc = base.resolve("automaticSrc");
+            tb.writeJavaFiles(automaticSrc, "package api; public class Api {}");
+            Path automaticClasses = base.resolve("automaticClasses");
+            tb.createDirectories(automaticClasses);
+
+            String automaticLog = new JavacTask(tb)
+                                    .outdir(automaticClasses)
+                                    .files(findJavaFiles(automaticSrc))
+                                    .run()
+                                    .writeAll()
+                                    .getOutput(Task.OutputKind.DIRECT);
+
+            if (!automaticLog.isEmpty())
+                throw new Exception("expected output not found: " + automaticLog);
+
+            Path automaticJar = modulePath.resolve("automatic-1.0.jar");
+
+            new JarTask(tb, automaticJar)
+              .baseDir(automaticClasses)
+              .files("api/Api.class")
+              .manifest("Automatic-Module-Name: " + name + "\n\n")
+              .run();
+
+            Path src = base.resolve("src");
+
+            tb.writeJavaFiles(src,
+                              "package impl; public class Impl { api.Api a; }");
+
+            Path classes = base.resolve("classes");
+
+            Files.createDirectories(classes);
+
+            List<String> log = new JavacTask(tb, Mode.CMDLINE)
+                    .options("--module-path", modulePath.toString(),
+                             "--add-modules", "ALL-MODULE-PATH",
+                             "-XDrawDiagnostics")
+                    .outdir(classes)
+                    .files(findJavaFiles(src))
+                    .run(Task.Expect.FAIL)
+                    .writeAll()
+                    .getOutputLines(Task.OutputKind.DIRECT);
+
+            List<String> expected =
+                    Arrays.asList("- compiler.err.locn.cant.get.module.name.for.jar: " +
+                                      "testAutomaticModuleNameIncorrect/module-path/automatic-1.0.jar".replace("/", File.separator),
+                                  "1 error");
+
+            if (!expected.equals(log)) {
+                throw new Exception("expected output not found: " + log);
+            }
+        }
+    }
+
+    @Test
+    public void testAutomaticModuleNameBroken(Path base) throws Exception {
+        Path modulePath = base.resolve("module-path");
+
+        Files.createDirectories(modulePath);
+
+        Path automaticSrc = base.resolve("automaticSrc");
+        tb.writeJavaFiles(automaticSrc, "package api; public class Api {}");
+        Path automaticClasses = base.resolve("automaticClasses");
+        tb.createDirectories(automaticClasses);
+
+        String automaticLog = new JavacTask(tb)
+                                .outdir(automaticClasses)
+                                .files(findJavaFiles(automaticSrc))
+                                .run()
+                                .writeAll()
+                                .getOutput(Task.OutputKind.DIRECT);
+
+        if (!automaticLog.isEmpty())
+            throw new Exception("expected output not found: " + automaticLog);
+
+        Path automaticJar = modulePath.resolve("automatic-1.0.jar");
+
+        try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(automaticJar))) {
+            out.putNextEntry(new ZipEntry("api/Api.class"));
+            Files.copy(automaticClasses.resolve("api").resolve("Api.class"), out);
+        }
+
+        Path src = base.resolve("src");
+
+        tb.writeJavaFiles(src,
+                          "module m { requires automatic; }",
+                          "package impl; public class Impl { api.Api a; }");
+
+        Path classes = base.resolve("classes");
+
+        Files.createDirectories(classes);
+
+        new JavacTask(tb)
+                .options("--module-path", modulePath.toString(),
+                         "-XDrawDiagnostics")
+                .outdir(classes)
+                .files(findJavaFiles(src))
+                .run(Task.Expect.SUCCESS)
+                .writeAll()
+                .getOutputLines(Task.OutputKind.DIRECT);
+
+        tb.writeJavaFiles(src,
+                          "module m { requires custom.module.name; }");
+
+        List<String> log = new JavacTask(tb)
+                .options("--module-path", modulePath.toString(),
+                         "-XDrawDiagnostics")
+                .outdir(classes)
+                .files(findJavaFiles(src))
+                .run(Task.Expect.FAIL)
+                .writeAll()
+                .getOutputLines(Task.OutputKind.DIRECT);
+
+        List<String> expected =
+                Arrays.asList("module-info.java:1:34: compiler.err.module.not.found: custom.module.name",
+                              "1 error");
+
+        if (!expected.equals(log)) {
+            throw new Exception("expected output not found: " + log);
+        }
+    }
+
 }
--- a/langtools/test/tools/javac/modules/PackageConflictTest.java	Thu Jun 15 17:24:13 2017 +0000
+++ b/langtools/test/tools/javac/modules/PackageConflictTest.java	Fri Jun 16 09:21:38 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -277,4 +277,57 @@
             throw new Exception("expected output not found");
         }
     }
+
+    @Test
+    public void testConflictInDependenciesInUnnamed(Path base) throws Exception {
+        Path msp = base.resolve("module-path-source");
+        Path m1 = msp.resolve("m1x");
+        Path m2 = msp.resolve("m2x");
+        tb.writeJavaFiles(m1,
+                          "module m1x { exports test; }",
+                          "package test; public class A { }");
+        tb.writeJavaFiles(m2,
+                          "module m2x { exports test; }",
+                          "package test; public class B { }");
+        Path mp = base.resolve("module-path");
+        Files.createDirectories(mp);
+
+        new JavacTask(tb)
+            .options("--module-source-path", msp.toString())
+            .outdir(mp)
+            .files(findJavaFiles(msp))
+            .run()
+            .writeAll();
+
+        Path src = base.resolve("src");
+
+        tb.writeJavaFiles(src,
+                          "package impl; public class Impl { }");
+
+        Path out = base.resolve("out");
+        Files.createDirectories(out);
+
+        List<String> log = new JavacTask(tb)
+                       .options("-XDrawDiagnostic",
+                                "--module-path", mp.toString(),
+                                "--add-modules", "ALL-MODULE-PATH")
+                       .outdir(out)
+                       .files(findJavaFiles(src))
+                       .run(Task.Expect.FAIL)
+                       .writeAll()
+                       .getOutputLines(Task.OutputKind.DIRECT);
+
+        List<String> expected1 =
+                Arrays.asList("error: the unnamed module reads package test from both m1x and m2x",
+                              "1 error");
+
+        List<String> expected2 =
+                Arrays.asList("error: the unnamed module reads package test from both m2x and m1x",
+                              "1 error");
+
+        if (!expected1.equals(log) && !expected2.equals(log)) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+    }
+
 }