--- old/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java 2016-12-15 09:19:12.883125246 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java 2016-12-15 09:19:12.753116352 +0000 @@ -26,7 +26,9 @@ package jdk.internal.module; import java.io.File; +import java.io.IOException; import java.io.PrintStream; +import java.io.UncheckedIOException; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; @@ -35,6 +37,7 @@ import java.lang.reflect.Layer; import java.lang.reflect.Module; import java.net.URI; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -46,11 +49,13 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; +import java.util.stream.Stream; import jdk.internal.loader.BootLoader; import jdk.internal.loader.BuiltinClassLoader; import jdk.internal.misc.SharedSecrets; import jdk.internal.perf.PerfCounter; +import jdk.internal.reflect.Reflection; /** * Initializes/boots the module system. @@ -195,7 +200,9 @@ // 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 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. if (mainModule == null || addAllDefaultModules) { boolean hasJava = false; if (systemModules.find(JAVA_SE).isPresent()) { @@ -212,6 +219,9 @@ if (hasJava && mn.startsWith("java.")) continue; + if (ModuleResolution.doNotResolveByDefault(mref)) + continue; + // add as root if observable and exports at least one package if ((finder == systemModules || finder.find(mn).isPresent())) { ModuleDescriptor descriptor = mref.descriptor(); @@ -231,6 +241,7 @@ ModuleFinder f = finder; // observable modules systemModules.findAll() .stream() + .filter(mref -> !ModuleResolution.doNotResolveByDefault(mref)) .map(ModuleReference::descriptor) .map(ModuleDescriptor::name) .filter(mn -> f.find(mn).isPresent()) // observable @@ -277,6 +288,8 @@ // time to create configuration PerfCounters.resolveTime.addElapsedTimeFrom(t3); + // check module names and incubating status + checkModuleNamesAndStatus(cf); // mapping of modules to class loaders Function clf = ModuleLoaderMap.mappingFunction(cf); @@ -496,8 +509,49 @@ if (!extraOpens.isEmpty()) { addExtraExportsOrOpens(bootLayer, extraOpens, true); } + + // DEBUG_ADD_OPENS is for debugging purposes only + String home = System.getProperty("java.home"); + Path file = Paths.get(home, "conf", "DEBUG_ADD_OPENS"); + if (Files.exists(file)) { + warn(file + " detected; may break encapsulation"); + try (Stream lines = Files.lines(file)) { + lines.map(line -> line.trim()) + .filter(line -> (!line.isEmpty() && !line.startsWith("#"))) + .forEach(line -> { + String[] s = line.split("/"); + if (s.length != 2) { + fail("Unable to parse as /: " + line); + } else { + String mn = s[0]; + String pkg = s[1]; + openPackage(bootLayer, mn, pkg); + } + }); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + Reflection.enableStackTraces(); + } } + private static void openPackage(Layer bootLayer, String mn, String pkg) { + if (mn.equals("ALL-RESOLVED") && pkg.equals("ALL-PACKAGES")) { + bootLayer.modules().stream().forEach(m -> + m.getDescriptor().packages().forEach(pn -> openPackage(m, pn))); + } else { + bootLayer.findModule(mn) + .filter(m -> m.getDescriptor().packages().contains(pkg)) + .ifPresent(m -> openPackage(m, pkg)); + } + } + + private static void openPackage(Module m, String pn) { + Modules.addOpensToAllUnnamed(m, pn); + warn("Opened for deep reflection: " + m.getName() + "/" + pn); + } + + private static void addExtraExportsOrOpens(Layer bootLayer, Map> map, boolean opens) @@ -508,12 +562,12 @@ String key = e.getKey(); String[] s = key.split("/"); if (s.length != 2) - fail("Unable to parse: " + key); + fail("Unable to parse as /: " + key); String mn = s[0]; String pn = s[1]; if (mn.isEmpty() || pn.isEmpty()) - fail("Module and package name must be specified:" + key); + fail("Module and package name must be specified: " + key); // The exporting module is in the boot layer Module m; @@ -585,7 +639,7 @@ int pos = value.indexOf('='); if (pos == -1) - fail("Unable to parse: " + value); + fail("Unable to parse as =: " + value); if (pos == 0) fail("Missing module name in: " + value); @@ -594,7 +648,7 @@ String rhs = value.substring(pos+1); if (rhs.isEmpty()) - fail("Unable to parse: " + value); + fail("Unable to parse as =: " + value); // value is (,)* or ()* if (!allowDuplicates && map.containsKey(key)) @@ -627,6 +681,33 @@ } /** + * Checks the names and resolution bit of each module in the configuration, + * emitting warnings if needed. + */ + private static void checkModuleNamesAndStatus(Configuration cf) { + String incubating = null; + for (ResolvedModule rm : cf.modules()) { + ModuleReference mref = rm.reference(); + String mn = mref.descriptor().name(); + + // emit warning if module name ends with a non-Java letter + //if (!Checks.hasLegalModuleNameLastCharacter(mn)) + // warn("Module name \"" + mn + "\" may soon be illegal"); + + // emit warning if the WARN_INCUBATING module resolution bit set + if (ModuleResolution.hasIncubatingWarning(mref)) { + if (incubating == null) { + incubating = mn; + } else { + incubating += ", " + mn; + } + } + } + if (incubating != null) + warn("using incubating module(s): " + incubating); + } + + /** * Throws a RuntimeException with the given message */ static void fail(String m) {