8181087: Module system implementation refresh (6/2017)
Reviewed-by: jjg
Contributed-by: alan.bateman@oracle.com, jan.lahoda@oracle.com
--- 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);
+ }
+ }
+
}