--- old/src/java.base/share/classes/java/lang/module/package-info.java 2018-06-19 20:55:48.000000000 +0100 +++ new/src/java.base/share/classes/java/lang/module/package-info.java 2018-06-19 20:55:48.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, 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 @@ -154,8 +154,9 @@ * application module specified to the 'java' launcher. When compiling code in * the unnamed module, or at run-time when the main application class is loaded * from the class path, then the default set of root modules is implementation - * specific (In the JDK implementation it is the module "java.se", if observable, - * and every observable module that exports an API).

+ * specific. In the JDK the default set of root modules contains every module + * that is observable on the upgrade module path or among the system modules, + * and that exports at least one package without qualification.

* *

Observable modules

* --- old/src/java.base/share/classes/jdk/internal/module/DefaultRoots.java 2018-06-19 20:55:50.000000000 +0100 +++ new/src/java.base/share/classes/jdk/internal/module/DefaultRoots.java 2018-06-19 20:55:49.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, 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 @@ -22,13 +22,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package jdk.internal.module; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; -import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; /** * Defines methods to compute the default set of root modules for the unnamed @@ -36,58 +37,45 @@ */ public final class DefaultRoots { - private static final String JAVA_SE = "java.se"; - private DefaultRoots() { } /** - * Returns the default set of root modules for the unnamed module computed from - * the system modules observable with the given module finder. + * Returns the default set of root modules for the unnamed module from the + * modules observable with the intersection of two module finders. + * + * The first module finder should be the module finder that finds modules on + * the upgrade module path or among the system modules. The second module + * finder should be the module finder that finds all modules on the module + * path, or a subset of when using --limit-modules. */ - static Set compute(ModuleFinder systemModuleFinder, ModuleFinder finder) { - Set roots = new HashSet<>(); - - boolean hasJava = false; - if (systemModuleFinder.find(JAVA_SE).isPresent()) { - if (finder == systemModuleFinder || finder.find(JAVA_SE).isPresent()) { - // java.se is a system module - hasJava = true; - roots.add(JAVA_SE); - } - } - - for (ModuleReference mref : systemModuleFinder.findAll()) { - String mn = mref.descriptor().name(); - if (hasJava && mn.startsWith("java.")) { - // not a root - continue; - } - - if (ModuleResolution.doNotResolveByDefault(mref)) { - // not a root - continue; - } - - if ((finder == systemModuleFinder || finder.find(mn).isPresent())) { - // add as root if exports at least one package to all modules - ModuleDescriptor descriptor = mref.descriptor(); - for (ModuleDescriptor.Exports e : descriptor.exports()) { - if (!e.isQualified()) { - roots.add(mn); - break; - } - } - } - } - - return roots; + static Set compute(ModuleFinder finder1, ModuleFinder finder2) { + return finder1.findAll().stream() + .filter(mref -> !ModuleResolution.doNotResolveByDefault(mref)) + .map(ModuleReference::descriptor) + .filter(descriptor -> finder2.find(descriptor.name()).isPresent() + && exportsAPI(descriptor)) + .map(ModuleDescriptor::name) + .collect(Collectors.toSet()); } /** * Returns the default set of root modules for the unnamed module from the * modules observable with the given module finder. + * + * This method is used by the jlink system modules plugin. */ public static Set compute(ModuleFinder finder) { return compute(finder, finder); } + + /** + * Returns true if the given module exports a package to all modules + */ + private static boolean exportsAPI(ModuleDescriptor descriptor) { + return descriptor.exports() + .stream() + .filter(e -> !e.isQualified()) + .findAny() + .isPresent(); + } } --- old/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java 2018-06-19 20:55:51.000000000 +0100 +++ new/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java 2018-06-19 20:55:51.000000000 +0100 @@ -278,11 +278,10 @@ // If there is no initial module specified then assume that the initial // module is the unnamed module of the application class loader. This - // is implemented by resolving "java.se" and all (non-java.*) modules - // that export an API. If "java.se" is not observable then all java.* - // modules are resolved. Modules that have the DO_NOT_RESOLVE_BY_DEFAULT - // bit set in their ModuleResolution attribute flags are excluded from - // the default set of roots. + // is implemented by resolving all observable modules that export an + // API. Modules that have the DO_NOT_RESOLVE_BY_DEFAULT bit set in + // their ModuleResolution attribute flags are excluded from the + // default set of roots. if (mainModule == null || addAllDefaultModules) { roots.addAll(DefaultRoots.compute(systemModuleFinder, finder)); } --- old/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java 2018-06-19 20:55:53.000000000 +0100 +++ new/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java 2018-06-19 20:55:53.000000000 +0100 @@ -81,6 +81,7 @@ import com.sun.tools.javac.code.Types; import com.sun.tools.javac.jvm.ClassWriter; import com.sun.tools.javac.jvm.JNIWriter; +import com.sun.tools.javac.jvm.Target; import com.sun.tools.javac.main.Option; import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.Warnings; @@ -144,6 +145,7 @@ private final JavaFileManager fileManager; private final ModuleFinder moduleFinder; private final Source source; + private final Target target; private final boolean allowModules; private final boolean allowAccessIntoSystem; @@ -191,6 +193,7 @@ types = Types.instance(context); fileManager = context.get(JavaFileManager.class); source = Source.instance(context); + target = Target.instance(context); allowModules = Feature.MODULES.allowedInSource(source); Options options = Options.instance(context); @@ -1234,18 +1237,24 @@ Set enabledRoot = new LinkedHashSet<>(); if (rootModules.contains(syms.unnamedModule)) { - ModuleSymbol javaSE = syms.getModule(java_se); Predicate jdkModulePred; - - if (javaSE != null && (observable == null || observable.contains(javaSE))) { + if (target.allApiModulesAreRoots()) { jdkModulePred = sym -> { sym.complete(); - return !sym.name.startsWith(java_) - && sym.exports.stream().anyMatch(e -> e.modules == null); + return sym.exports.stream().anyMatch(e -> e.modules == null); }; - enabledRoot.add(javaSE); } else { - jdkModulePred = sym -> true; + ModuleSymbol javaSE = syms.getModule(java_se); + if (javaSE != null && (observable == null || observable.contains(javaSE))) { + jdkModulePred = sym -> { + sym.complete(); + return !sym.name.startsWith(java_) + && sym.exports.stream().anyMatch(e -> e.modules == null); + }; + enabledRoot.add(javaSE); + } else { + jdkModulePred = sym -> true; + } } Predicate noIncubatorPred = sym -> { --- old/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java 2018-06-19 20:55:55.000000000 +0100 +++ new/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java 2018-06-19 20:55:55.000000000 +0100 @@ -160,4 +160,11 @@ public String multiReleaseValue() { return Integer.toString(this.ordinal() - Target.JDK1_1.ordinal() + 1); } + + /** All modules that export an API are roots when compiling code in the unnamed + * module and targeting 11 or newer. + */ + public boolean allApiModulesAreRoots() { + return compareTo(JDK1_11) >= 0; + } } --- old/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java 2018-06-19 20:55:57.000000000 +0100 +++ new/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java 2018-06-19 20:55:57.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, 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 @@ -62,6 +62,7 @@ import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; import java.util.stream.Stream; public class JdepsConfiguration implements AutoCloseable { @@ -319,7 +320,6 @@ static class SystemModuleFinder implements ModuleFinder { private static final String JAVA_HOME = System.getProperty("java.home"); - private static final String JAVA_SE = "java.se"; private final FileSystem fileSystem; private final Path root; @@ -444,29 +444,15 @@ } public Set defaultSystemRoots() { - Set roots = new HashSet<>(); - boolean hasJava = false; - if (systemModules.containsKey(JAVA_SE)) { - // java.se is a system module - hasJava = true; - roots.add(JAVA_SE); - } - - for (ModuleReference mref : systemModules.values()) { - String mn = mref.descriptor().name(); - if (hasJava && mn.startsWith("java.")) - continue; - - // add as root if observable and exports at least one package - ModuleDescriptor descriptor = mref.descriptor(); - for (ModuleDescriptor.Exports e : descriptor.exports()) { - if (!e.isQualified()) { - roots.add(mn); - break; - } - } - } - return roots; + return systemModules.values().stream() + .map(ModuleReference::descriptor) + .filter(descriptor -> descriptor.exports() + .stream() + .filter(e -> !e.isQualified()) + .findAny() + .isPresent()) + .map(ModuleDescriptor::name) + .collect(Collectors.toSet()); } } --- /dev/null 2018-06-19 20:55:59.000000000 +0100 +++ new/test/jdk/jdk/modules/etc/DefaultModules.java 2018-06-19 20:55:59.000000000 +0100 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018, 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. + */ + +/** + * @test + * @bug 8197532 + * @modules jdk.compiler + * jdk.jlink + * jdk.zipfs + * @library src /lib/testlibrary + * @build java.json/* + * @run main DefaultModules + * @summary Test that all modules that export an API are in the set of modules + * resolved when compiling or running code on the class path + */ + +import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import java.util.spi.ToolProvider; + +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.OutputAnalyzer; + +/** + * This test compiles and runs the following tests on the class path: + * + * TestRootModules.java.java - tests that every module that exports an API + * is resolved. Also tests that java.se is not resolved. + * + * TestJson.java - exercises APIs exported by the java.json module. The + * java.json module is not a Java SE module. + */ + +public class DefaultModules { + private static final PrintStream out = System.out; + + public static void main(String[] args) throws Exception { + String javaHome = System.getProperty("java.home"); + String testSrc = System.getProperty("test.src"); + + // $JDK_HOME/bin/java TestModules.java + String source = Path.of(testSrc, "src", "TestRootModules.java").toString(); + ProcessTools.executeTestJava(source) + .outputTo(System.out) + .errorTo(System.err) + .shouldHaveExitValue(0); + + /** + * Create a run-time image containing java.se, java.json and the javac + * compiler. Use the run-time image to compile and run both + * TestModules.java and JsonTest.java + */ + if (Files.exists(Path.of(javaHome, "jmods", "java.se.jmod"))) { + // jlink --add-modules java.se,java.json,jdk.compiler,jdk.zipfs + Path here = Path.of("."); + Path image = Files.createTempDirectory(here, "images").resolve("myimage"); + ToolProvider jlink = ToolProvider.findFirst("jlink") + .orElseThrow(() -> new RuntimeException("jlink not found")); + int exitCode = jlink.run(System.out, System.err, + "--module-path", System.getProperty("test.module.path"), + "--add-modules", "java.se,java.json,jdk.compiler,jdk.zipfs", + "--output", image.toString()); + if (exitCode != 0) + throw new RuntimeException("jlink failed"); + + // path to java launcher in run-time image + String javaLauncher = image.resolve("bin").resolve("java").toString(); + if (System.getProperty("os.name").startsWith("Windows")) + javaLauncher += ".exe"; + + // $CUSTOM_JDK/bin/java TestRootModules.java + source = Path.of(testSrc, "src", "TestRootModules.java").toString(); + out.format("Command line: [%s %s]%n", javaLauncher, source); + ProcessTools.executeProcess(new ProcessBuilder(javaLauncher, source)) + .outputTo(System.out) + .errorTo(System.err) + .shouldHaveExitValue(0); + + // $CUSTOM_JDK/bin/java TestJson.java + source = Path.of(testSrc, "src", "TestJson.java").toString(); + out.format("Command line: [%s %s]%n", javaLauncher, source); + ProcessTools.executeProcess(new ProcessBuilder(javaLauncher, source)) + .outputTo(System.out) + .errorTo(System.err) + .shouldHaveExitValue(0); + } + } +} --- /dev/null 2018-06-19 20:56:01.000000000 +0100 +++ new/test/jdk/jdk/modules/etc/src/TestJson.java 2018-06-19 20:56:01.000000000 +0100 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018, 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. + */ + +import javax.json.*; +import java.io.InputStream; + +/** + * Exercise APIs exported by the java.json module + */ + +public class TestJson { + public static void main(String[] args) { + JsonParser parser = Json.createParser(InputStream.nullInputStream()); + } +} --- /dev/null 2018-06-19 20:56:03.000000000 +0100 +++ new/test/jdk/jdk/modules/etc/src/TestRootModules.java 2018-06-19 20:56:02.000000000 +0100 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018, 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. + */ + + +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; + +/** + * Test the set of modules in the boot layer includes all modules that export + * an API. Also test that java.se is not resolved. + */ + +public class TestRootModules { + public static void main(String[] args) { + // all modules that export an API should be resolved + // For now, this test ignores the ModuleResolution attribute + ModuleLayer bootLayer = ModuleLayer.boot(); + ModuleFinder.ofSystem().findAll().stream() + .map(ModuleReference::descriptor) + .filter(descriptor -> descriptor.exports() + .stream() + .filter(e -> !e.isQualified()) + .findAny() + .isPresent()) + .map(ModuleDescriptor::name) + .forEach(name -> { + if (!bootLayer.findModule(name).isPresent()) + throw new RuntimeException(name + " not in boot layer"); + }); + + // java.se should not be resolved + ModuleLayer.boot() + .findModule("java.se") + .map(m -> { throw new RuntimeException("java.se should not be resolved"); }); + } +} --- /dev/null 2018-06-19 20:56:04.000000000 +0100 +++ new/test/jdk/jdk/modules/etc/src/java.json/javax/json/Json.java 2018-06-19 20:56:04.000000000 +0100 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018, 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 javax.json; + +import java.io.InputStream; + +public class Json { + private Json() { } + + public static JsonParser createParser(InputStream in) { + return new JsonParser() { }; + } +} --- /dev/null 2018-06-19 20:56:06.000000000 +0100 +++ new/test/jdk/jdk/modules/etc/src/java.json/javax/json/JsonParser.java 2018-06-19 20:56:06.000000000 +0100 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018, 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 javax.json; + +public interface JsonParser { +} --- /dev/null 2018-06-19 20:56:08.000000000 +0100 +++ new/test/jdk/jdk/modules/etc/src/java.json/module-info.java 2018-06-19 20:56:07.000000000 +0100 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018, 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 java.json { + exports javax.json; +}