8169069: Module system implementation refresh (11/2016)

Co-authored-by: Mandy Chung <mandy.chung@oracle.com>
Co-authored-by: Claes Redestad <claes.redestad@oracle.com>
Co-authored-by: Mark Reinhold <mark.reinhold@oracle.com>
Reviewed-by: plevart, chegar, psandoz, mchung, alanb, dfuchs, naoto, coffeys, weijun
This commit is contained in:
Alan Bateman 2016-12-01 08:57:53 +00:00
parent 2e931825c6
commit fbe85300bf
510 changed files with 16787 additions and 5365 deletions
jdk
make
src
java.base/share
java.compact1/share/classes
java.compact2/share/classes
java.compact3/share/classes
java.desktop/share/classes
java.instrument/share/classes
java.logging/share/classes/java/util/logging
java.management/share/classes
java.se.ee/share/classes
java.se/share/classes
java.sql.rowset/share/classes
java.sql/share/classes
java.transaction/share/classes
java.xml.crypto/share/classes
jdk.accessibility/share/classes
jdk.attach/share/classes
jdk.desktop/share/classes
jdk.internal.le/share/classes
jdk.internal.opt/share/classes
jdk.jartool/share/classes/sun/tools/jar
jdk.jconsole/share/classes
jdk.jdi
jdk.jdwp.agent/share/classes

@ -2709,22 +2709,6 @@ JDWP "Java(tm) Debug Wire Protocol"
(Error VM_DEAD)
)
)
(Command CanRead=3
"Returns true if this module can read the source module; false otherwise."
"<p>Since JDWP version 9."
(Out
(moduleID module "This module.")
(moduleID sourceModule "The source module.")
)
(Reply
(boolean canRead "true if this module can read the source module; false otherwise.")
)
(ErrorSet
(Error INVALID_MODULE "This module or sourceModule is not the ID of a module.")
(Error NOT_IMPLEMENTED)
(Error VM_DEAD)
)
)
)
(CommandSet Event=64
(Command Composite=100

@ -27,7 +27,8 @@ include LauncherCommon.gmk
$(eval $(call SetupBuildLauncher, jconsole, \
MAIN_CLASS := sun.tools.jconsole.JConsole, \
JAVA_ARGS := -Djconsole.showOutputViewer, \
JAVA_ARGS := --add-opens java.base/java.io=jdk.jconsole \
-Djconsole.showOutputViewer, \
CFLAGS_windows := -DJAVAW, \
LIBS_windows := user32.lib, \
))

@ -65,7 +65,7 @@ public class AddPackagesAttribute {
String mn = entry.getFileName().toString();
Optional<ModuleReference> omref = finder.find(mn);
if (omref.isPresent()) {
Set<String> packages = omref.get().descriptor().conceals();
Set<String> packages = omref.get().descriptor().packages();
addPackagesAttribute(mi, packages);
}
}
@ -77,7 +77,7 @@ public class AddPackagesAttribute {
byte[] bytes;
try (InputStream in = Files.newInputStream(mi)) {
ModuleInfoExtender extender = ModuleInfoExtender.newExtender(in);
extender.conceals(packages);
extender.packages(packages);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
extender.write(baos);
bytes = baos.toByteArray();

@ -43,7 +43,7 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;
import static java.lang.module.ModuleDescriptor.Requires.Modifier.PUBLIC;
import static java.lang.module.ModuleDescriptor.Requires.Modifier.TRANSITIVE;
/**
* Generate the DOT file for a module graph for each module in the JDK
@ -172,14 +172,14 @@ public class GenGraphs {
Graph<String> graph = gengraph(cf);
descriptors.forEach(md -> {
String mn = md.name();
Set<String> requiresPublic = md.requires().stream()
.filter(d -> d.modifiers().contains(PUBLIC))
Set<String> requiresTransitive = md.requires().stream()
.filter(d -> d.modifiers().contains(TRANSITIVE))
.map(d -> d.name())
.collect(Collectors.toSet());
graph.adjacentNodes(mn).forEach(dn -> {
String attr = dn.equals("java.base") ? REQUIRES_BASE
: (requiresPublic.contains(dn) ? REEXPORTS : REQUIRES);
: (requiresTransitive.contains(dn) ? REEXPORTS : REQUIRES);
int w = weightOf(mn, dn);
if (w > 1)
attr += "weight=" + w;
@ -194,8 +194,8 @@ public class GenGraphs {
/**
* Returns a Graph of the given Configuration after transitive reduction.
*
* Transitive reduction of requires public edge and requires edge have
* to be applied separately to prevent the requires public edges
* Transitive reduction of requires transitive edge and requires edge have
* to be applied separately to prevent the requires transitive edges
* (e.g. U -> V) from being reduced by a path (U -> X -> Y -> V)
* in which V would not be re-exported from U.
*/
@ -208,21 +208,21 @@ public class GenGraphs {
.map(ResolvedModule::name)
.forEach(target -> builder.addEdge(mn, target));
}
Graph<String> rpg = requiresPublicGraph(cf);
Graph<String> rpg = requiresTransitiveGraph(cf);
return builder.build().reduce(rpg);
}
/**
* Returns a Graph containing only requires public edges
* Returns a Graph containing only requires transitive edges
* with transitive reduction.
*/
private Graph<String> requiresPublicGraph(Configuration cf) {
private Graph<String> requiresTransitiveGraph(Configuration cf) {
Graph.Builder<String> builder = new Graph.Builder<>();
for (ResolvedModule resolvedModule : cf.modules()) {
ModuleDescriptor descriptor = resolvedModule.reference().descriptor();
String mn = descriptor.name();
descriptor.requires().stream()
.filter(d -> d.modifiers().contains(PUBLIC))
.filter(d -> d.modifiers().contains(TRANSITIVE))
.map(d -> d.name())
.forEach(d -> builder.addEdge(mn, d));
}

@ -299,7 +299,7 @@ public class ModuleSummary {
static class HtmlDocument {
final String title;
final Map<String, ModuleSummary> modules;
boolean requiresPublicNote = false;
boolean requiresTransitiveNote = false;
boolean aggregatorNote = false;
boolean totalBytesNote = false;
HtmlDocument(String title, Map<String, ModuleSummary> modules) {
@ -510,16 +510,16 @@ public class ModuleSummary {
public String requiresColumn() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("<td>"));
boolean footnote = requiresPublicNote;
boolean footnote = requiresTransitiveNote;
ms.descriptor().requires().stream()
.sorted(Comparator.comparing(Requires::name))
.forEach(r -> {
boolean requiresPublic = r.modifiers().contains(Requires.Modifier.PUBLIC);
Selector sel = requiresPublic ? REQUIRES_PUBLIC : REQUIRES;
boolean requiresTransitive = r.modifiers().contains(Requires.Modifier.TRANSITIVE);
Selector sel = requiresTransitive ? REQUIRES_PUBLIC : REQUIRES;
String req = String.format("<a class=\"%s\" href=\"#%s\">%s</a>",
sel, r.name(), r.name());
if (!requiresPublicNote && requiresPublic) {
requiresPublicNote = true;
if (!requiresTransitiveNote && requiresTransitive) {
requiresTransitiveNote = true;
req += "<sup>*</sup>";
}
sb.append(req).append("\n").append("<br>");
@ -534,8 +534,8 @@ public class ModuleSummary {
sb.append("<br>");
sb.append("<i>+").append(indirectDeps).append(" transitive dependencies</i>");
}
if (footnote != requiresPublicNote) {
sb.append("<br><br>").append("<i>* bold denotes requires public</i>");
if (footnote != requiresTransitiveNote) {
sb.append("<br><br>").append("<i>* bold denotes requires transitive</i>");
}
sb.append("</td>");
return sb.toString();
@ -558,11 +558,10 @@ public class ModuleSummary {
ms.descriptor().uses().stream()
.sorted()
.forEach(s -> sb.append("uses ").append(s).append("<br>").append("\n"));
ms.descriptor().provides().entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.flatMap(e -> e.getValue().providers().stream()
.map(p -> String.format("provides %s<br>&nbsp;&nbsp;&nbsp;&nbsp;with %s",
e.getKey(), p)))
ms.descriptor().provides().stream()
.sorted(Comparator.comparing(Provides::service))
.map(p -> String.format("provides %s<br>&nbsp;&nbsp;&nbsp;&nbsp;with %s",
p.service(), p.providers()))
.forEach(p -> sb.append(p).append("<br>").append("\n"));
sb.append("</td>");
return sb.toString();

@ -30,219 +30,593 @@ import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.stream.Collectors.*;
/**
* A build tool to extend the module-info.java in the source tree for
* platform-specific exports, uses, and provides and write to the specified
* output file. Injecting platform-specific requires is not supported.
* platform-specific exports, opens, uses, and provides and write to
* the specified output file.
*
* The extra exports, uses, provides can be specified in module-info.java.extra
* files and GenModuleInfoSource will be invoked for each module that has
* GenModuleInfoSource will be invoked for each module that has
* module-info.java.extra in the source directory.
*
* The extra exports, opens, uses, provides can be specified
* in module-info.java.extra.
* Injecting platform-specific requires is not supported.
*
* @see build.tools.module.ModuleInfoExtraTest for basic testing
*/
public class GenModuleInfoSource {
private final static String USAGE =
"Usage: GenModuleInfoSource [option] -o <output file> <module-info-java>\n" +
"Options are:\n" +
" -exports <package-name>\n" +
" -exports <package-name>[/<module-name>]\n" +
" -uses <service>\n" +
" -provides <service>/<provider-impl-classname>\n";
"Usage: GenModuleInfoSource -o <output file> \n" +
" --source-file <module-info-java>\n" +
" --modules <module-name>[,<module-name>...]\n" +
" <module-info.java.extra> ...\n";
static boolean verbose = false;
public static void main(String... args) throws Exception {
Path outfile = null;
Path moduleInfoJava = null;
GenModuleInfoSource genModuleInfo = new GenModuleInfoSource();
Set<String> modules = Collections.emptySet();
List<Path> extras = new ArrayList<>();
// validate input arguments
for (int i = 0; i < args.length; i++){
String option = args[i];
if (option.startsWith("-")) {
String arg = args[++i];
if (option.equals("-exports")) {
int index = arg.indexOf('/');
if (index > 0) {
String pn = arg.substring(0, index);
String mn = arg.substring(index + 1, arg.length());
genModuleInfo.exportTo(pn, mn);
} else {
genModuleInfo.export(arg);
}
} else if (option.equals("-uses")) {
genModuleInfo.use(arg);
} else if (option.equals("-provides")) {
int index = arg.indexOf('/');
if (index <= 0) {
throw new IllegalArgumentException("invalid -provide argument: " + arg);
}
String service = arg.substring(0, index);
String impl = arg.substring(index + 1, arg.length());
genModuleInfo.provide(service, impl);
} else if (option.equals("-o")) {
String arg = i+1 < args.length ? args[i+1] : null;
switch (option) {
case "-o":
outfile = Paths.get(arg);
} else {
throw new IllegalArgumentException("invalid option: " + option);
}
} else if (moduleInfoJava != null) {
throw new IllegalArgumentException("more than one module-info.java");
} else {
moduleInfoJava = Paths.get(option);
if (Files.notExists(moduleInfoJava)) {
throw new IllegalArgumentException(option + " not exist");
}
i++;
break;
case "--source-file":
moduleInfoJava = Paths.get(arg);
if (Files.notExists(moduleInfoJava)) {
throw new IllegalArgumentException(moduleInfoJava + " not exist");
}
i++;
break;
case "--modules":
modules = Arrays.stream(arg.split(","))
.collect(toSet());
i++;
break;
case "-v":
verbose = true;
break;
default:
Path file = Paths.get(option);
if (Files.notExists(file)) {
throw new IllegalArgumentException(file + " not exist");
}
extras.add(file);
}
}
if (moduleInfoJava == null || outfile == null) {
if (moduleInfoJava == null || outfile == null ||
modules.isEmpty() || extras.isEmpty()) {
System.err.println(USAGE);
System.exit(-1);
}
GenModuleInfoSource genModuleInfo =
new GenModuleInfoSource(moduleInfoJava, extras, modules);
// generate new module-info.java
genModuleInfo.generate(moduleInfoJava, outfile);
genModuleInfo.generate(outfile);
}
private final Set<String> exports = new HashSet<>();
private final Map<String, Set<String>> exportsTo = new HashMap<>();
private final Set<String> uses = new HashSet<>();
private final Map<String, Set<String>> provides = new HashMap<>();
GenModuleInfoSource() {
}
final Path sourceFile;
final List<Path> extraFiles;
final ModuleInfo extras;
final Set<String> modules;
final ModuleInfo moduleInfo;
GenModuleInfoSource(Path sourceFile, List<Path> extraFiles, Set<String> modules)
throws IOException
{
this.sourceFile = sourceFile;
this.extraFiles = extraFiles;
this.modules = modules;
this.moduleInfo = new ModuleInfo();
this.moduleInfo.parse(sourceFile);
private void export(String p) {
Objects.requireNonNull(p);
if (exports.contains(p) || exportsTo.containsKey(p)) {
throw new RuntimeException("duplicated exports: " + p);
// parse module-info.java.extra
this.extras = new ModuleInfo();
for (Path file : extraFiles) {
extras.parse(file);
}
exports.add(p);
}
private void exportTo(String p, String mn) {
Objects.requireNonNull(p);
Objects.requireNonNull(mn);
if (exports.contains(p)) {
throw new RuntimeException("unqualified exports already exists: " + p);
}
exportsTo.computeIfAbsent(p, _k -> new HashSet<>()).add(mn);
// merge with module-info.java.extra
moduleInfo.augmentModuleInfo(extras, modules);
}
private void use(String service) {
uses.add(service);
}
private void provide(String s, String impl) {
provides.computeIfAbsent(s, _k -> new HashSet<>()).add(impl);
}
private void generate(Path sourcefile, Path outfile) throws IOException {
Path parent = outfile.getParent();
if (parent != null)
Files.createDirectories(parent);
List<String> lines = Files.readAllLines(sourcefile);
try (BufferedWriter bw = Files.newBufferedWriter(outfile);
void generate(Path output) throws IOException {
List<String> lines = Files.readAllLines(sourceFile);
try (BufferedWriter bw = Files.newBufferedWriter(output);
PrintWriter writer = new PrintWriter(bw)) {
int lineNumber = 0;
// write the copyright header and lines up to module declaration
for (String l : lines) {
lineNumber++;
String[] s = l.trim().split("\\s+");
String keyword = s[0].trim();
int nextIndex = keyword.length();
String exp = null;
int n = l.length();
switch (keyword) {
case "exports":
boolean inExportsTo = false;
// assume package name immediately after exports
exp = s[1].trim();
if (s.length >= 3) {
nextIndex = l.indexOf(exp, nextIndex) + exp.length();
if (s[2].trim().equals("to")) {
inExportsTo = true;
n = l.indexOf("to", nextIndex) + "to".length();
} else {
throw new RuntimeException(sourcefile + ", line " +
lineNumber + ", is malformed: " + s[2]);
}
}
// inject the extra targets after "to"
if (inExportsTo) {
writer.println(injectExportTargets(exp, l, n));
} else {
writer.println(l);
}
break;
case "to":
if (exp == null) {
throw new RuntimeException(sourcefile + ", line " +
lineNumber + ", is malformed");
}
n = l.indexOf("to", nextIndex) + "to".length();
writer.println(injectExportTargets(exp, l, n));
break;
case "}":
doAugments(writer);
// fall through
default:
writer.println(l);
// reset exports
exp = null;
writer.println(l);
if (l.trim().startsWith("module ")) {
writer.format(" // source file: %s%n", sourceFile);
for (Path file: extraFiles) {
writer.format(" // %s%n", file);
}
break;
}
}
// requires
for (String l : lines) {
if (l.trim().startsWith("requires"))
writer.println(l);
}
// write exports, opens, uses, and provides
moduleInfo.print(writer);
// close
writer.println("}");
}
}
private String injectExportTargets(String pn, String exp, int pos) {
Set<String> targets = exportsTo.remove(pn);
if (targets != null) {
StringBuilder sb = new StringBuilder();
// inject the extra targets after the given pos
sb.append(exp.substring(0, pos))
.append("\n\t")
.append(targets.stream()
.collect(Collectors.joining(",", "", ",")))
.append(" /* injected */");
if (pos < exp.length()) {
// print the remaining statement followed "to"
sb.append("\n\t")
.append(exp.substring(pos+1, exp.length()));
class ModuleInfo {
final Map<String, Statement> exports = new HashMap<>();
final Map<String, Statement> opens = new HashMap<>();
final Map<String, Statement> uses = new HashMap<>();
final Map<String, Statement> provides = new HashMap<>();
Statement getStatement(String directive, String name) {
switch (directive) {
case "exports":
if (moduleInfo.exports.containsKey(name) &&
moduleInfo.exports.get(name).isUnqualified()) {
throw new IllegalArgumentException(sourceFile +
" already has " + directive + " " + name);
}
return exports.computeIfAbsent(name,
_n -> new Statement("exports", "to", name));
case "opens":
if (moduleInfo.opens.containsKey(name) &&
moduleInfo.opens.get(name).isUnqualified()) {
throw new IllegalArgumentException(sourceFile +
" already has " + directive + " " + name);
}
if (moduleInfo.opens.containsKey(name)) {
throw new IllegalArgumentException(sourceFile +
" already has " + directive + " " + name);
}
return opens.computeIfAbsent(name,
_n -> new Statement("opens", "to", name));
case "uses":
return uses.computeIfAbsent(name,
_n -> new Statement("uses", "", name));
case "provides":
return provides.computeIfAbsent(name,
_n -> new Statement("provides", "with", name, true));
default:
throw new IllegalArgumentException(directive);
}
}
/*
* Augment this ModuleInfo with module-info.java.extra
*/
void augmentModuleInfo(ModuleInfo extraFiles, Set<String> modules) {
// API package exported in the original module-info.java
extraFiles.exports.entrySet()
.stream()
.filter(e -> exports.containsKey(e.getKey()) &&
e.getValue().filter(modules))
.forEach(e -> mergeExportsOrOpens(exports.get(e.getKey()),
e.getValue(),
modules));
// add exports that are not defined in the original module-info.java
extraFiles.exports.entrySet()
.stream()
.filter(e -> !exports.containsKey(e.getKey()) &&
e.getValue().filter(modules))
.forEach(e -> addTargets(getStatement("exports", e.getKey()),
e.getValue(),
modules));
// API package opened in the original module-info.java
extraFiles.opens.entrySet()
.stream()
.filter(e -> opens.containsKey(e.getKey()) &&
e.getValue().filter(modules))
.forEach(e -> mergeExportsOrOpens(opens.get(e.getKey()),
e.getValue(),
modules));
// add opens that are not defined in the original module-info.java
extraFiles.opens.entrySet()
.stream()
.filter(e -> !opens.containsKey(e.getKey()) &&
e.getValue().filter(modules))
.forEach(e -> addTargets(getStatement("opens", e.getKey()),
e.getValue(),
modules));
// provides
extraFiles.provides.keySet()
.stream()
.filter(service -> provides.containsKey(service))
.forEach(service -> mergeProvides(service,
extraFiles.provides.get(service)));
extraFiles.provides.keySet()
.stream()
.filter(service -> !provides.containsKey(service))
.forEach(service -> provides.put(service,
extraFiles.provides.get(service)));
// uses
extraFiles.uses.keySet()
.stream()
.filter(service -> !uses.containsKey(service))
.forEach(service -> uses.put(service, extraFiles.uses.get(service)));
}
// add qualified exports or opens to known modules only
private void addTargets(Statement statement,
Statement extra,
Set<String> modules)
{
extra.targets.stream()
.filter(mn -> modules.contains(mn))
.forEach(mn -> statement.addTarget(mn));
}
private void mergeExportsOrOpens(Statement statement,
Statement extra,
Set<String> modules)
{
String pn = statement.name;
if (statement.isUnqualified() && extra.isQualified()) {
throw new RuntimeException("can't add qualified exports to " +
"unqualified exports " + pn);
}
Set<String> mods = extra.targets.stream()
.filter(mn -> statement.targets.contains(mn))
.collect(toSet());
if (mods.size() > 0) {
throw new RuntimeException("qualified exports " + pn + " to " +
mods.toString() + " already declared in " + sourceFile);
}
// add qualified exports or opens to known modules only
addTargets(statement, extra, modules);
}
private void mergeProvides(String service, Statement extra) {
Statement statement = provides.get(service);
Set<String> mods = extra.targets.stream()
.filter(mn -> statement.targets.contains(mn))
.collect(toSet());
if (mods.size() > 0) {
throw new RuntimeException("qualified exports " + service + " to " +
mods.toString() + " already declared in " + sourceFile);
}
extra.targets.stream()
.forEach(mn -> statement.addTarget(mn));
}
void print(PrintWriter writer) {
// print unqualified exports
exports.entrySet().stream()
.filter(e -> e.getValue().targets.isEmpty())
.sorted(Map.Entry.comparingByKey())
.forEach(e -> writer.println(e.getValue()));
// print qualified exports
exports.entrySet().stream()
.filter(e -> !e.getValue().targets.isEmpty())
.sorted(Map.Entry.comparingByKey())
.forEach(e -> writer.println(e.getValue()));
// print unqualified opens
opens.entrySet().stream()
.filter(e -> e.getValue().targets.isEmpty())
.sorted(Map.Entry.comparingByKey())
.forEach(e -> writer.println(e.getValue()));
// print qualified opens
opens.entrySet().stream()
.filter(e -> !e.getValue().targets.isEmpty())
.sorted(Map.Entry.comparingByKey())
.forEach(e -> writer.println(e.getValue()));
// uses and provides
writer.println();
uses.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(e -> writer.println(e.getValue()));
provides.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(e -> writer.println(e.getValue()));
}
private void parse(Path sourcefile) throws IOException {
List<String> lines = Files.readAllLines(sourcefile);
Statement statement = null;
boolean hasTargets = false;
for (int lineNumber = 1; lineNumber <= lines.size(); ) {
String l = lines.get(lineNumber-1).trim();
int index = 0;
if (l.isEmpty()) {
lineNumber++;
continue;
}
// comment block starts
if (l.startsWith("/*")) {
while (l.indexOf("*/") == -1) { // end comment block
l = lines.get(lineNumber++).trim();
}
index = l.indexOf("*/") + 2;
if (index >= l.length()) {
lineNumber++;
continue;
} else {
// rest of the line
l = l.substring(index, l.length()).trim();
index = 0;
}
}
// skip comment and annotations
if (l.startsWith("//") || l.startsWith("@")) {
lineNumber++;
continue;
}
int current = lineNumber;
int count = 0;
while (index < l.length()) {
if (current == lineNumber && ++count > 20)
throw new Error("Fail to parse line " + lineNumber + " " + sourcefile);
int end = l.indexOf(';');
if (end == -1)
end = l.length();
String content = l.substring(0, end).trim();
if (content.isEmpty()) {
index = end+1;
if (index < l.length()) {
// rest of the line
l = l.substring(index, l.length()).trim();
index = 0;
}
continue;
}
String[] s = content.split("\\s+");
String keyword = s[0].trim();
String name = s.length > 1 ? s[1].trim() : null;
trace("%d: %s index=%d len=%d%n", lineNumber, l, index, l.length());
switch (keyword) {
case "module":
case "requires":
case "}":
index = l.length(); // skip to the end
continue;
case "exports":
case "opens":
case "provides":
case "uses":
// assume name immediately after exports, opens, provides, uses
statement = getStatement(keyword, name);
hasTargets = false;
int i = l.indexOf(name, keyword.length()+1) + name.length() + 1;
l = i < l.length() ? l.substring(i, l.length()).trim() : "";
index = 0;
if (s.length >= 3) {
if (!s[2].trim().equals(statement.qualifier)) {
throw new RuntimeException(sourcefile + ", line " +
lineNumber + ", is malformed: " + s[2]);
}
}
break;
case "to":
case "with":
if (statement == null) {
throw new RuntimeException(sourcefile + ", line " +
lineNumber + ", is malformed");
}
hasTargets = true;
String qualifier = statement.qualifier;
i = l.indexOf(qualifier, index) + qualifier.length() + 1;
l = i < l.length() ? l.substring(i, l.length()).trim() : "";
index = 0;
break;
}
if (index >= l.length()) {
// skip to next line
continue;
}
// comment block starts
if (l.startsWith("/*")) {
while (l.indexOf("*/") == -1) { // end comment block
l = lines.get(lineNumber++).trim();
}
index = l.indexOf("*/") + 2;
if (index >= l.length()) {
continue;
} else {
// rest of the line
l = l.substring(index, l.length()).trim();
index = 0;
}
}
if (l.startsWith("//")) {
index = l.length();
continue;
}
if (statement == null) {
throw new RuntimeException(sourcefile + ", line " +
lineNumber + ": missing keyword?");
}
if (!hasTargets) {
continue;
}
if (index >= l.length()) {
throw new RuntimeException(sourcefile + ", line " +
lineNumber + ": " + l);
}
// parse the target module of exports, opens, or provides
Statement stmt = statement;
int terminal = l.indexOf(';', index);
// determine up to which position to parse
int pos = terminal != -1 ? terminal : l.length();
// parse up to comments
int pos1 = l.indexOf("//", index);
if (pos1 != -1 && pos1 < pos) {
pos = pos1;
}
int pos2 = l.indexOf("/*", index);
if (pos2 != -1 && pos2 < pos) {
pos = pos2;
}
// target module(s) for qualitifed exports or opens
// or provider implementation class(es)
String rhs = l.substring(index, pos).trim();
index += rhs.length();
trace("rhs: index=%d [%s] [line: %s]%n", index, rhs, l);
String[] targets = rhs.split(",");
for (String t : targets) {
String n = t.trim();
if (n.length() > 0)
stmt.addTarget(n);
}
// start next statement
if (pos == terminal) {
statement = null;
hasTargets = false;
index = terminal + 1;
}
l = index < l.length() ? l.substring(index, l.length()).trim() : "";
index = 0;
}
lineNumber++;
}
}
}
static class Statement {
final String directive;
final String qualifier;
final String name;
final Set<String> targets = new LinkedHashSet<>();
final boolean ordered;
Statement(String directive, String qualifier, String name) {
this(directive, qualifier, name, false);
}
Statement(String directive, String qualifier, String name, boolean ordered) {
this.directive = directive;
this.qualifier = qualifier;
this.name = name;
this.ordered = ordered;
}
Statement addTarget(String mn) {
if (mn.isEmpty())
throw new IllegalArgumentException("empty module name");
targets.add(mn);
return this;
}
boolean isQualified() {
return targets.size() > 0;
}
boolean isUnqualified() {
return targets.isEmpty();
}
/**
* Returns true if this statement is unqualified or it has
* at least one target in the given names.
*/
boolean filter(Set<String> names) {
if (isUnqualified()) {
return true;
} else {
return targets.stream()
.filter(mn -> names.contains(mn))
.findAny().isPresent();
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(" ");
sb.append(directive).append(" ").append(name);
if (targets.isEmpty()) {
sb.append(";");
} else if (targets.size() == 1) {
sb.append(" ").append(qualifier)
.append(orderedTargets().collect(joining(",", " ", ";")));
} else {
sb.append(" ").append(qualifier)
.append(orderedTargets()
.map(target -> String.format(" %s", target))
.collect(joining(",\n", "\n", ";")));
}
return sb.toString();
} else {
return exp;
}
public Stream<String> orderedTargets() {
return ordered ? targets.stream()
: targets.stream().sorted();
}
}
private void doAugments(PrintWriter writer) {
if ((exports.size() + exportsTo.size() + uses.size() + provides.size()) == 0)
return;
writer.println(" // augmented from module-info.java.extra");
exports.stream()
.sorted()
.forEach(e -> writer.format(" exports %s;%n", e));
// remaining injected qualified exports
exportsTo.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(e -> String.format(" exports %s to%n%s;", e.getKey(),
e.getValue().stream().sorted()
.map(mn -> String.format(" %s", mn))
.collect(Collectors.joining(",\n"))))
.forEach(writer::println);
uses.stream().sorted()
.forEach(s -> writer.format(" uses %s;%n", s));
provides.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.flatMap(e -> e.getValue().stream().sorted()
.map(impl -> String.format(" provides %s with %s;",
e.getKey(), impl)))
.forEach(writer::println);
static void trace(String fmt, Object... params) {
if (verbose) {
System.out.format(fmt, params);
}
}
}

@ -0,0 +1,243 @@
/*
* Copyright (c) 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 build.tools.module;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import build.tools.module.GenModuleInfoSource.Statement;
/**
* Sanity test for GenModuleInfoSource tool
*/
public class ModuleInfoExtraTest {
private static final Path DIR = Paths.get("test");
public static void main(String... args) throws Exception {
if (args.length != 0)
GenModuleInfoSource.verbose = true;
ModuleInfoExtraTest test = new ModuleInfoExtraTest("m", "m1", "m2", "m3");
test.run();
}
String[] moduleInfo = new String[] {
"exports p",
"to",
" // comment",
" /* comment */ m1",
",",
"m2,m3",
" ;",
"exports q to m1;",
"provides s with /* ",
" comment */ impl ; // comment",
"provides s1",
" with ",
" impl1, impl2;"
};
String[] moduleInfoExtra = new String[] {
"exports q",
"to",
" m2 // comment",
" /* comment */;",
" ;",
"opens p.q ",
" to /* comment */ m3",
" , // m1",
" /* comment */, m4;",
"provides s1 with impl3;"
};
String[] test1 = new String[] {
"exports p1 to m1;",
"exports p2"
};
String[] test2 = new String[] {
"exports to m1;"
};
String[] test3 = new String[]{
"exports p3 to m1;",
" m2, m3;"
};
String[] test4 = new String[]{
"provides s with impl1;", // typo ; should be ,
" impl2, impl3;"
};
String[] test5 = new String[]{
"uses s3",
"provides s3 with impl1,",
" impl2, impl3;"
};
final Builder builder;
ModuleInfoExtraTest(String name, String... modules) {
this.builder = new Builder(name).modules(modules);
}
void run() throws IOException {
testModuleInfo();
errorCases();
}
void testModuleInfo() throws IOException {
GenModuleInfoSource source = builder.sourceFile(moduleInfo).build();
Set<String> targetsP = new HashSet<>();
targetsP.add("m1");
targetsP.add("m2");
targetsP.add("m3");
Set<String> targetsQ = new HashSet<>();
targetsQ.add("m1");
Set<String> providerS = new HashSet<>();
providerS.add("impl");
Set<String> providerS1 = new HashSet<>();
providerS1.add("impl1");
providerS1.add("impl2");
Set<String> opensPQ = new HashSet<>();
check(source, targetsP, targetsQ, opensPQ, providerS, providerS1);
// augment with extra
Path file = DIR.resolve("extra");
Files.write(file, Arrays.asList(moduleInfoExtra));
source = builder.build(file);
targetsQ.add("m2");
providerS1.add("impl3");
opensPQ.add("m3");
check(source, targetsP, targetsQ, opensPQ, providerS, providerS1);
}
void check(GenModuleInfoSource source,
Set<String> targetsP,
Set<String> targetsQ,
Set<String> opensPQ,
Set<String> providerS,
Set<String> providerS1) {
source.moduleInfo.print(new PrintWriter(System.out, true));
Statement export = source.moduleInfo.exports.get("p");
if (!export.targets.equals(targetsP)) {
throw new Error("unexpected: " + export);
}
export = source.moduleInfo.exports.get("q");
if (!export.targets.equals(targetsQ)) {
throw new Error("unexpected: " + export);
}
Statement provides = source.moduleInfo.provides.get("s");
if (!provides.targets.equals(providerS)) {
throw new Error("unexpected: " + provides);
}
provides = source.moduleInfo.provides.get("s1");
if (!provides.targets.equals(providerS1)) {
throw new Error("unexpected: " + provides);
}
}
void errorCases() throws IOException {
fail(test1);
fail(test2);
fail(test3);
fail(test4);
fail(test5);
}
void fail(String... extras) throws IOException {
Path file = DIR.resolve("test1");
Files.write(file, Arrays.asList(extras));
try {
builder.build(file);
} catch (RuntimeException e) {
if (!e.getMessage().matches("test/test1, line .* is malformed.*") &&
!e.getMessage().matches("test/test1, line .* missing keyword.*")) {
throw e;
}
}
}
static class Builder {
final String moduleName;
final Path sourceFile;
final Set<String> modules = new HashSet<>();
public Builder(String name) {
this.moduleName = name;
this.sourceFile = DIR.resolve(name).resolve("module-info.java");
}
public Builder modules(String... names) {
Arrays.stream(names).forEach(modules::add);
return this;
}
public Builder sourceFile(String... lines) throws IOException {
Files.createDirectories(sourceFile.getParent());
try (BufferedWriter bw = Files.newBufferedWriter(sourceFile);
PrintWriter writer = new PrintWriter(bw)) {
writer.format("module %s {%n", moduleName);
for (String l : lines) {
writer.println(l);
}
writer.println("}");
}
return this;
}
public GenModuleInfoSource build() throws IOException {
return build(Collections.emptyList());
}
public GenModuleInfoSource build(Path extraFile) throws IOException {
return build(Collections.singletonList(extraFile));
}
public GenModuleInfoSource build(List<Path> extraFiles) throws IOException {
return new GenModuleInfoSource(sourceFile, extraFiles, modules);
}
}
}

@ -15,12 +15,12 @@ pack.class.attribute.SourceID = RUH
pack.class.attribute.CompilationID = RUH
# Module attributes, supported by the tool and not JSR-200
pack.class.attribute.Module = NH[RUHFH]NH[RUHNH[RUH]]NH[RCH]NH[RCHRCH]
pack.class.attribute.ConcealedPackages = NH[RUH]
pack.class.attribute.Version = RUH
pack.class.attribute.MainClass = RCH
pack.class.attribute.TargetPlatform = RUHRUHRUH
pack.class.attribute.Hashes = RUHNH[RUHRUH]
pack.class.attribute.Module = RUHFHNH[RUHFH]NH[RUHFHNH[RUH]]NH[RUHFHNH[RUH]]NH[RCH]NH[RCHNH[RCH]]
pack.class.attribute.ModulePackages = NH[RUH]
pack.class.attribute.ModuleVersion = RUH
pack.class.attribute.ModuleMainClass = RCH
pack.class.attribute.ModuleTarget = RUHRUHRUH
pack.class.attribute.ModuleHashes = RUHNH[RUHNH[B]]
# Note: Zero-length ("marker") attributes do not need to be specified here.

@ -26,46 +26,55 @@
package java.lang;
import java.lang.annotation.Annotation;
import java.lang.module.ModuleDescriptor.Version;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReader;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Member;
import java.lang.reflect.Field;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Module;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Proxy;
import java.lang.ref.SoftReference;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectStreamField;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Layer;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Module;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.loader.BootLoader;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.loader.ResourceHelper;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
import jdk.internal.module.ModuleHashes;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.ConstantPool;
import jdk.internal.reflect.Reflection;
@ -442,21 +451,10 @@ public final class Class<T> implements java.io.Serializable,
PrivilegedAction<ClassLoader> pa = module::getClassLoader;
ClassLoader cl = AccessController.doPrivileged(pa);
if (module.isNamed() && cl != null) {
return cl.loadLocalClass(module, name);
}
final Class<?> c;
if (cl != null) {
c = cl.loadLocalClass(name);
return cl.loadClass(module, name);
} else {
c = BootLoader.loadClassOrNull(name);
}
if (c != null && c.getModule() == module) {
return c;
} else {
return null;
return BootLoader.loadClass(module, name);
}
}
@ -979,7 +977,7 @@ public final class Class<T> implements java.io.Serializable,
}
// cached package name
private String packageName;
private transient String packageName;
/**
* Returns the interfaces directly implemented by the class or interface
@ -1976,6 +1974,22 @@ public final class Class<T> implements java.io.Serializable,
return method;
}
/**
* Returns a {@code Method} object that reflects the specified public
* member method of the class or interface represented by this
* {@code Class} object.
*
* @param name the name of the method
* @param parameterTypes the list of parameters
* @return the {@code Method} object that matches the specified
* {@code name} and {@code parameterTypes}; {@code null}
* if the method is not found or the name is
* "&lt;init&gt;"or "&lt;clinit&gt;".
*/
Method getMethodOrNull(String name, Class<?>... parameterTypes) {
return getMethod0(name, parameterTypes, true);
}
/**
* Returns a {@code Constructor} object that reflects the specified
@ -2367,12 +2381,17 @@ public final class Class<T> implements java.io.Serializable,
}
/**
* Finds a resource with a given name. If this class is in a named {@link
* Module Module}, and the caller of this method is in the same module,
* then this method will attempt to find the resource in that module.
* Otherwise, the rules for searching resources
* associated with a given class are implemented by the defining
* {@linkplain ClassLoader class loader} of the class. This method
* Finds a resource with a given name.
*
* <p> If this class is in a named {@link Module Module} then this method
* will attempt to find the resource in the module by means of the absolute
* resource name, subject to the rules for encapsulation specified in the
* {@code Module} {@link Module#getResourceAsStream getResourceAsStream}
* method.
*
* <p> Otherwise, if this class is not in a named module then the rules for
* searching resources associated with a given class are implemented by the
* defining {@linkplain ClassLoader class loader} of the class. This method
* delegates to this object's class loader. If this object was loaded by
* the bootstrap class loader, the method delegates to {@link
* ClassLoader#getSystemResourceAsStream}.
@ -2400,8 +2419,11 @@ public final class Class<T> implements java.io.Serializable,
* </ul>
*
* @param name name of the desired resource
* @return A {@link java.io.InputStream} object or {@code null} if
* no resource with this name is found
* @return A {@link java.io.InputStream} object; {@code null} if no
* resource with this name is found, the resource is in a package
* that is not {@link Module#isOpen(String, Module) open} to at
* least the caller module, or access to the resource is denied
* by the security manager.
* @throws NullPointerException If {@code name} is {@code null}
* @since 1.1
*/
@ -2409,35 +2431,41 @@ public final class Class<T> implements java.io.Serializable,
public InputStream getResourceAsStream(String name) {
name = resolveName(name);
// if this Class and the caller are in the same named module
// then attempt to get an input stream to the resource in the
// module
Module module = getModule();
if (module.isNamed()) {
Class<?> caller = Reflection.getCallerClass();
if (caller != null && caller.getModule() == module) {
ClassLoader cl = getClassLoader0();
String mn = module.getName();
try {
// special-case built-in class loaders to avoid the
// need for a URL connection
if (cl == null) {
return BootLoader.findResourceAsStream(mn, name);
} else if (cl instanceof BuiltinClassLoader) {
return ((BuiltinClassLoader) cl).findResourceAsStream(mn, name);
} else {
URL url = cl.findResource(mn, name);
return (url != null) ? url.openStream() : null;
if (!ResourceHelper.isSimpleResource(name)) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != module) {
Set<String> packages = module.getDescriptor().packages();
String pn = ResourceHelper.getPackageName(name);
if (packages.contains(pn) && !module.isOpen(pn, caller)) {
// resource is in package not open to caller
return null;
}
} catch (IOException | SecurityException e) {
return null;
}
}
String mn = module.getName();
ClassLoader cl = getClassLoader0();
try {
// special-case built-in class loaders to avoid the
// need for a URL connection
if (cl == null) {
return BootLoader.findResourceAsStream(mn, name);
} else if (cl instanceof BuiltinClassLoader) {
return ((BuiltinClassLoader) cl).findResourceAsStream(mn, name);
} else {
URL url = cl.findResource(mn, name);
return (url != null) ? url.openStream() : null;
}
} catch (IOException | SecurityException e) {
return null;
}
}
// this Class and caller not in the same named module
// unnamed module
ClassLoader cl = getClassLoader0();
if (cl == null) {
return ClassLoader.getSystemResourceAsStream(name);
@ -2447,12 +2475,17 @@ public final class Class<T> implements java.io.Serializable,
}
/**
* Finds a resource with a given name. If this class is in a named {@link
* Module Module}, and the caller of this method is in the same module,
* then this method will attempt to find the resource in that module.
* Otherwise, the rules for searching resources
* associated with a given class are implemented by the defining
* {@linkplain ClassLoader class loader} of the class. This method
* Finds a resource with a given name.
*
* <p> If this class is in a named {@link Module Module} then this method
* will attempt to find the resource in the module by means of the absolute
* resource name, subject to the rules for encapsulation specified in the
* {@code Module} {@link Module#getResourceAsStream getResourceAsStream}
* method.
*
* <p> Otherwise, if this class is not in a named module then the rules for
* searching resources associated with a given class are implemented by the
* defining {@linkplain ClassLoader class loader} of the class. This method
* delegates to this object's class loader. If this object was loaded by
* the bootstrap class loader, the method delegates to {@link
* ClassLoader#getSystemResource}.
@ -2479,35 +2512,46 @@ public final class Class<T> implements java.io.Serializable,
* </ul>
*
* @param name name of the desired resource
* @return A {@link java.net.URL} object; {@code null} if no
* resource with this name is found or the resource cannot
* be located by a URL.
* @return A {@link java.net.URL} object; {@code null} if no resource with
* this name is found, the resource cannot be located by a URL, the
* resource is in a package that is not
* {@link Module#isOpen(String, Module) open} to at least the caller
* module, or access to the resource is denied by the security
* manager.
* @throws NullPointerException If {@code name} is {@code null}
* @since 1.1
*/
@CallerSensitive
public URL getResource(String name) {
name = resolveName(name);
// if this Class and the caller are in the same named module
// then attempt to get URL to the resource in the module
Module module = getModule();
if (module.isNamed()) {
Class<?> caller = Reflection.getCallerClass();
if (caller != null && caller.getModule() == module) {
String mn = getModule().getName();
ClassLoader cl = getClassLoader0();
try {
if (cl == null) {
return BootLoader.findResource(mn, name);
} else {
return cl.findResource(mn, name);
if (!ResourceHelper.isSimpleResource(name)) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != module) {
Set<String> packages = module.getDescriptor().packages();
String pn = ResourceHelper.getPackageName(name);
if (packages.contains(pn) && !module.isOpen(pn, caller)) {
// resource is in package not open to caller
return null;
}
} catch (IOException ioe) {
return null;
}
}
String mn = getModule().getName();
ClassLoader cl = getClassLoader0();
try {
if (cl == null) {
return BootLoader.findResource(mn, name);
} else {
return cl.findResource(mn, name);
}
} catch (IOException ioe) {
return null;
}
}
// unnamed module
ClassLoader cl = getClassLoader0();
if (cl == null) {
return ClassLoader.getSystemResource(name);
@ -2632,9 +2676,6 @@ public final class Class<T> implements java.io.Serializable,
* if name is absolute
*/
private String resolveName(String name) {
if (name == null) {
return name;
}
if (!name.startsWith("/")) {
Class<?> c = this;
while (c.isArray()) {

@ -42,15 +42,14 @@ import java.security.cert.Certificate;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.Stack;
import java.util.NoSuchElementException;
import java.util.Vector;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
@ -59,7 +58,6 @@ import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.perf.PerfCounter;
import jdk.internal.module.ServicesCatalog;
import jdk.internal.loader.BootLoader;
import jdk.internal.loader.ClassLoaders;
import jdk.internal.misc.SharedSecrets;
@ -411,7 +409,6 @@ public abstract class ClassLoader {
this(checkCreateClassLoader(), null, parent);
}
/**
* Creates a new class loader using the <tt>ClassLoader</tt> returned by
* the method {@link #getSystemClassLoader()
@ -431,7 +428,6 @@ public abstract class ClassLoader {
this(checkCreateClassLoader(), null, getSystemClassLoader());
}
/**
* Returns the name of this class loader or {@code null} if
* this class loader is not named.
@ -581,7 +577,7 @@ public abstract class ClassLoader {
* @return The resulting {@code Class} object in a module defined by
* this class loader, or {@code null} if the class could not be found.
*/
final Class<?> loadLocalClass(Module module, String name) {
final Class<?> loadClass(Module module, String name) {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
@ -596,34 +592,6 @@ public abstract class ClassLoader {
}
}
/**
* Loads the class with the specified <a href="#name">binary name</a>
* defined by this class loader. This method returns {@code null}
* if the class could not be found.
*
* @apiNote This method does not delegate to the parent class loader.
*
* @param name
* The <a href="#name">binary name</a> of the class
*
* @return The resulting {@code Class} object in a module defined by
* this class loader, or {@code null} if the class could not be found.
*/
final Class<?> loadLocalClass(String name) {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
return findClass(name);
} catch (ClassNotFoundException e) {
// ignore
}
}
return c;
}
}
/**
* Returns the lock object for class loading operations.
* For backward compatibility, the default implementation of this method
@ -724,12 +692,17 @@ public abstract class ClassLoader {
* should override this method.
*
* @apiNote This method returns {@code null} rather than throwing
* {@code ClassNotFoundException} if the class could not be found
* {@code ClassNotFoundException} if the class could not be found.
*
* @implSpec The default implementation returns {@code null}.
* @implSpec The default implementation attempts to find the class by
* invoking {@link #findClass(String)} when the {@code moduleName} is
* {@code null}. It otherwise returns {@code null}.
*
* @param moduleName
* The module name
* The module name; or {@code null} to find the class in the
* {@linkplain #getUnnamedModule() unnamed module} for this
* class loader
* @param name
* The <a href="#name">binary name</a> of the class
*
@ -739,6 +712,11 @@ public abstract class ClassLoader {
* @since 9
*/
protected Class<?> findClass(String moduleName, String name) {
if (moduleName == null) {
try {
return findClass(name);
} catch (ClassNotFoundException ignore) { }
}
return null;
}
@ -1286,10 +1264,20 @@ public abstract class ClassLoader {
* Class loader implementations that support the loading from modules
* should override this method.
*
* @implSpec The default implementation returns {@code null}.
* @apiNote This method is the basis for the {@code Class} {@link
* Class#getResource getResource} and {@link Class#getResourceAsStream
* getResourceAsStream} methods. It is not subject to the rules for
* encapsulation specified by {@code Module} {@link
* Module#getResourceAsStream getResourceAsStream}.
*
* @implSpec The default implementation attempts to find the resource by
* invoking {@link #findResource(String)} when the {@code moduleName} is
* {@code null}. It otherwise returns {@code null}.
*
* @param moduleName
* The module name
* The module name; or {@code null} to find a resource in the
* {@linkplain #getUnnamedModule() unnamed module} for this
* class loader
* @param name
* The resource name
*
@ -1306,7 +1294,11 @@ public abstract class ClassLoader {
* @since 9
*/
protected URL findResource(String moduleName, String name) throws IOException {
return null;
if (moduleName == null) {
return findResource(name);
} else {
return null;
}
}
/**
@ -1314,9 +1306,6 @@ public abstract class ClassLoader {
* (images, audio, text, etc) that can be accessed by class code in a way
* that is independent of the location of the code.
*
* Resources in a named module are private to that module. This method does
* not find resource in named modules.
*
* <p> The name of a resource is a '<tt>/</tt>'-separated path name that
* identifies the resource.
*
@ -1325,16 +1314,30 @@ public abstract class ClassLoader {
* built-in to the virtual machine is searched. That failing, this method
* will invoke {@link #findResource(String)} to find the resource. </p>
*
* @apiNote When overriding this method it is recommended that an
* implementation ensures that any delegation is consistent with the {@link
* <p> Resources in named modules are subject to the encapsulation rules
* specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
* Additionally, and except for the special case where the resource has a
* name ending with "{@code .class}", this method will only find resources in
* packages of named modules when the package is {@link Module#isOpen(String)
* opened} unconditionally (even if the caller of this method is in the
* same module as the resource). </p>
*
* @apiNote Where several modules are defined to the same class loader,
* and where more than one module contains a resource with the given name,
* then the ordering that modules are searched is not specified and may be
* very unpredictable.
* When overriding this method it is recommended that an implementation
* ensures that any delegation is consistent with the {@link
* #getResources(java.lang.String) getResources(String)} method.
*
* @param name
* The resource name
*
* @return A <tt>URL</tt> object for reading the resource, or
* <tt>null</tt> if the resource could not be found or the invoker
* doesn't have adequate privileges to get the resource.
* @return {@code URL} object for reading the resource; {@code null} if
* the resource could not be found, a {@code URL} could not be
* constructed to locate the resource, the resource is in a package
* that is not opened unconditionally, or access to the resource is
* denied by the security manager.
*
* @since 1.1
*/
@ -1356,16 +1359,24 @@ public abstract class ClassLoader {
* (images, audio, text, etc) that can be accessed by class code in a way
* that is independent of the location of the code.
*
* Resources in a named module are private to that module. This method does
* not find resources in named modules.
*
* <p>The name of a resource is a <tt>/</tt>-separated path name that
* <p> The name of a resource is a <tt>/</tt>-separated path name that
* identifies the resource.
*
* <p> The search order is described in the documentation for {@link
* #getResource(String)}. </p>
* <p> The delegation order for searching is described in the documentation
* for {@link #getResource(String)}. </p>
*
* @apiNote When overriding this method it is recommended that an
* <p> Resources in named modules are subject to the encapsulation rules
* specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
* Additionally, and except for the special case where the resource has a
* name ending with "{@code .class}", this method will only find resources in
* packages of named modules when the package is {@link Module#isOpen(String)
* opened} unconditionally (even if the caller of this method is in the
* same module as the resource).</p>
*
* @apiNote Where several modules are defined to the same class loader,
* and where more than one module contains a resource with the given name,
* then the ordering is not specified and may be very unpredictable.
* When overriding this method it is recommended that an
* implementation ensures that any delegation is consistent with the {@link
* #getResource(java.lang.String) getResource(String)} method. This should
* ensure that the first element returned by the Enumeration's
@ -1376,9 +1387,11 @@ public abstract class ClassLoader {
* The resource name
*
* @return An enumeration of {@link java.net.URL <tt>URL</tt>} objects for
* the resource. If no resources could be found, the enumeration
* will be empty. Resources that the class loader doesn't have
* access to will not be in the enumeration.
* the resource. If no resources could be found, the enumeration
* will be empty. Resources for which a {@code URL} cannot be
* constructed, are in package that is not opened unconditionally,
* or access to the resource is denied by the security manager,
* are not returned in the enumeration.
*
* @throws IOException
* If I/O errors occur
@ -1406,9 +1419,6 @@ public abstract class ClassLoader {
* can be accessed by class code in a way that is independent of the
* location of the code.
*
* Resources in a named module are private to that module. This method does
* not find resources in named modules.
*
* <p> The name of a resource is a {@code /}-separated path name that
* identifies the resource.
*
@ -1420,6 +1430,14 @@ public abstract class ClassLoader {
* exception is wrapped in an {@link UncheckedIOException} that is then
* thrown.
*
* <p> Resources in named modules are subject to the encapsulation rules
* specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
* Additionally, and except for the special case where the resource has a
* name ending with "{@code .class}", this method will only find resources in
* packages of named modules when the package is {@link Module#isOpen(String)
* opened} unconditionally (even if the caller of this method is in the
* same module as the resource). </p>
*
* @apiNote When overriding this method it is recommended that an
* implementation ensures that any delegation is consistent with the {@link
* #getResource(java.lang.String) getResource(String)} method. This should
@ -1430,9 +1448,10 @@ public abstract class ClassLoader {
* The resource name
*
* @return A stream of resource {@link java.net.URL URL} objects. If no
* resources could be found, the stream will be empty. Resources
* that the class loader doesn't have access to will not be in the
* stream.
* resources could be found, the stream will be empty. Resources
* for which a {@code URL} cannot be constructed, are in a package
* that is not opened unconditionally, or access to the resource
* is denied by the security manager, will not be in the stream.
*
* @see #findResources(String)
*
@ -1455,14 +1474,21 @@ public abstract class ClassLoader {
* Finds the resource with the given name. Class loader implementations
* should override this method to specify where to find resources.
*
* Resources in a named module are private to that module. This method does
* not find resources in named modules defined to this class loader.
* <p> For resources in named modules then the method must implement the
* rules for encapsulation specified in the {@code Module} {@link
* Module#getResourceAsStream getResourceAsStream} method. Additionally,
* it must not find non-"{@code .class}" resources in packages of named
* modules unless the package is {@link Module#isOpen(String) opened}
* unconditionally. </p>
*
* @param name
* The resource name
*
* @return A <tt>URL</tt> object for reading the resource, or
* <tt>null</tt> if the resource could not be found
* @return {@code URL} object for reading the resource; {@code null} if
* the resource could not be found, a {@code URL} could not be
* constructed to locate the resource, the resource is in a package
* that is not opened unconditionally, or access to the resource is
* denied by the security manager.
*
* @since 1.2
*/
@ -1476,14 +1502,22 @@ public abstract class ClassLoader {
* implementations should override this method to specify where to load
* resources from.
*
* Resources in a named module are private to that module. This method does
* not find resources in named modules defined to this class loader.
* <p> For resources in named modules then the method must implement the
* rules for encapsulation specified in the {@code Module} {@link
* Module#getResourceAsStream getResourceAsStream} method. Additionally,
* it must not find non-"{@code .class}" resources in packages of named
* modules unless the package is {@link Module#isOpen(String) opened}
* unconditionally. </p>
*
* @param name
* The resource name
*
* @return An enumeration of {@link java.net.URL <tt>URL</tt>} objects for
* the resources
* the resource. If no resources could be found, the enumeration
* will be empty. Resources for which a {@code URL} cannot be
* constructed, are in a package that is not opened unconditionally,
* or access to the resource is denied by the security manager,
* are not returned in the enumeration.
*
* @throws IOException
* If I/O errors occur
@ -1491,7 +1525,7 @@ public abstract class ClassLoader {
* @since 1.2
*/
protected Enumeration<URL> findResources(String name) throws IOException {
return java.util.Collections.emptyEnumeration();
return Collections.emptyEnumeration();
}
/**
@ -1541,14 +1575,21 @@ public abstract class ClassLoader {
* classes. This method locates the resource through the system class
* loader (see {@link #getSystemClassLoader()}).
*
* Resources in a named module are private to that module. This method does
* not find resources in named modules.
* <p> Resources in named modules are subject to the encapsulation rules
* specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
* Additionally, and except for the special case where the resource has a
* name ending with "{@code .class}", this method will only find resources in
* packages of named modules when the package is {@link Module#isOpen(String)
* opened} unconditionally. </p>
*
* @param name
* The resource name
*
* @return A {@link java.net.URL <tt>URL</tt>} object for reading the
* resource, or <tt>null</tt> if the resource could not be found
* @return A {@link java.net.URL <tt>URL</tt>} to the resource; {@code
* null} if the resource could not be found, a URL could not be
* constructed to locate the resource, the resource is in a package
* that is not opened unconditionally or access to the resource is
* denied by the security manager.
*
* @since 1.1
*/
@ -1562,17 +1603,25 @@ public abstract class ClassLoader {
* {@link java.util.Enumeration <tt>Enumeration</tt>} of {@link
* java.net.URL <tt>URL</tt>} objects.
*
* Resources in a named module are private to that module. This method does
* not find resources in named modules.
*
* <p> The search order is described in the documentation for {@link
* #getSystemResource(String)}. </p>
*
* <p> Resources in named modules are subject to the encapsulation rules
* specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
* Additionally, and except for the special case where the resource has a
* name ending with "{@code .class}", this method will only find resources in
* packages of named modules when the package is {@link Module#isOpen(String)
* opened} unconditionally. </p>
*
* @param name
* The resource name
*
* @return An enumeration of resource {@link java.net.URL <tt>URL</tt>}
* objects
* @return An enumeration of {@link java.net.URL <tt>URL</tt>} objects for
* the resource. If no resources could be found, the enumeration
* will be empty. Resources for which a {@code URL} cannot be
* constructed, are in a package that is not opened unconditionally,
* or access to the resource is denied by the security manager,
* are not returned in the enumeration.
*
* @throws IOException
* If I/O errors occur
@ -1588,17 +1637,23 @@ public abstract class ClassLoader {
/**
* Returns an input stream for reading the specified resource.
*
* Resources in a named module are private to that module. This method does
* not find resources in named modules.
*
* <p> The search order is described in the documentation for {@link
* #getResource(String)}. </p>
*
* <p> Resources in named modules are subject to the encapsulation rules
* specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
* Additionally, and except for the special case where the resource has a
* name ending with "{@code .class}", this method will only find resources in
* packages of named modules when the package is {@link Module#isOpen(String)
* opened} unconditionally. </p>
*
* @param name
* The resource name
*
* @return An input stream for reading the resource, or <tt>null</tt>
* if the resource could not be found
* @return An input stream for reading the resource; {@code null} if the
* resource could not be found, the resource is in a package that
* is not opened unconditionally, or access to the resource is
* denied by the security manager.
*
* @since 1.1
*/
@ -1616,14 +1671,20 @@ public abstract class ClassLoader {
* used to load classes. This method locates the resource through the
* system class loader (see {@link #getSystemClassLoader()}).
*
* Resources in a named module are private to that module. This method does
* not find resources in named modules.
* <p> Resources in named modules are subject to the encapsulation rules
* specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
* Additionally, and except for the special case where the resource has a
* name ending with "{@code .class}", this method will only find resources in
* packages of named modules when the package is {@link Module#isOpen(String)
* opened} unconditionally. </p>
*
* @param name
* The resource name
*
* @return An input stream for reading the resource, or <tt>null</tt>
* if the resource could not be found
* @return An input stream for reading the resource; {@code null} if the
* resource could not be found, the resource is in a package that
* is not opened unconditionally, or access to the resource is
* denied by the security manager.
*
* @since 1.1
*/
@ -2709,33 +2770,7 @@ public abstract class ClassLoader {
private static native AssertionStatusDirectives retrieveDirectives();
/**
* Returns the ServiceCatalog for modules defined to this class loader
* or {@code null} if this class loader does not have a services catalog.
*/
ServicesCatalog getServicesCatalog() {
return servicesCatalog;
}
/**
* Returns the ServiceCatalog for modules defined to this class loader,
* creating it if it doesn't already exist.
*/
ServicesCatalog createOrGetServicesCatalog() {
ServicesCatalog catalog = servicesCatalog;
if (catalog == null) {
catalog = ServicesCatalog.create();
boolean set = trySetObjectField("servicesCatalog", catalog);
if (!set) {
// beaten by someone else
catalog = servicesCatalog;
}
}
return catalog;
}
// the ServiceCatalog for modules associated with this class loader.
private volatile ServicesCatalog servicesCatalog;
// -- Misc --
/**
* Returns the ConcurrentHashMap used as a storage for ClassLoaderValue(s)

@ -40,6 +40,11 @@ import static java.lang.annotation.ElementType.*;
* annotation on a local variable declaration or on a parameter declaration
* or a package declaration has no effect on the warnings issued by a compiler.
*
* <p>When a module is deprecated, the use of that module in {@code
* requires}, but not in {@code exports} or {@code opens} clauses causes
* a warning to be issued. A module being deprecated does <em>not</em> cause
* warnings to be issued for uses of types within the module.
*
* <p>This annotation type has a string-valued element {@code since}. The value
* of this element indicates the version in which the annotated program element
* was first deprecated.
@ -74,7 +79,7 @@ import static java.lang.annotation.ElementType.*;
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {
/**
* Returns the version in which the annotated element became deprecated.

@ -398,10 +398,16 @@ public class Package extends NamedPackage implements java.lang.reflect.Annotated
if (packageInfo == null) {
// find package-info.class defined by loader
String cn = packageName() + ".package-info";
PrivilegedAction<ClassLoader> pa = module()::getClassLoader;
Module module = module();
PrivilegedAction<ClassLoader> pa = module::getClassLoader;
ClassLoader loader = AccessController.doPrivileged(pa);
Class<?> c = loader != null ? loader.loadLocalClass(cn)
: BootLoader.loadClassOrNull(cn);
Class<?> c;
if (loader != null) {
c = loader.loadClass(module, cn);
} else {
c = BootLoader.loadClass(module, cn);
}
if (c != null) {
packageInfo = c;
} else {

@ -35,6 +35,9 @@ import static java.lang.annotation.ElementType.*;
* a superset of the warnings suppressed in all containing elements. For
* example, if you annotate a class to suppress one warning and annotate a
* method to suppress another, both warnings will be suppressed in the method.
* However, note that if a warning is suppressed in a {@code
* module-info} file, the suppression applies to elements within the
* file and <em>not</em> to types contained within the module.
*
* <p>As a matter of style, programmers should always use this annotation
* on the most deeply nested element where it is effective. If you want to
@ -49,7 +52,7 @@ import static java.lang.annotation.ElementType.*;
* @jls 5.5.2 Checked Casts and Unchecked Casts
* @jls 9.6.4.5 @SuppressWarnings
*/
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
/**

@ -38,6 +38,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Layer;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Module;
import java.net.URL;
@ -69,7 +70,6 @@ import jdk.internal.logger.LazyLoggers;
import jdk.internal.logger.LocalizedLoggerWrapper;
import jdk.internal.module.ModuleBootstrap;
import jdk.internal.module.ServicesCatalog;
/**
* The <code>System</code> class contains several useful class fields
@ -1987,7 +1987,10 @@ public final class System {
private static void setJavaLangAccess() {
// Allow privileged classes outside of java.lang
SharedSecrets.setJavaLangAccess(new JavaLangAccess(){
SharedSecrets.setJavaLangAccess(new JavaLangAccess() {
public Method getMethodOrNull(Class<?> klass, String name, Class<?>... parameterTypes) {
return klass.getMethodOrNull(name, parameterTypes);
}
public jdk.internal.reflect.ConstantPool getConstantPool(Class<?> klass) {
return klass.getConstantPool();
}
@ -2031,12 +2034,6 @@ public final class System {
public Layer getBootLayer() {
return bootLayer;
}
public ServicesCatalog getServicesCatalog(ClassLoader cl) {
return cl.getServicesCatalog();
}
public ServicesCatalog createOrGetServicesCatalog(ClassLoader cl) {
return cl.createOrGetServicesCatalog();
}
public ConcurrentHashMap<?, ?> createOrGetClassLoaderValueMap(ClassLoader cl) {
return cl.createOrGetClassLoaderValueMap();
}

@ -123,27 +123,25 @@ class VersionProps {
/**
* In case you were wondering this method is called by java -version.
* Sad that it prints to stderr; would be nicer if default printed on
* stdout.
*/
public static void print() {
print(System.err);
public static void print(boolean err) {
print(err, false);
}
/**
* This is the same as print except that it adds an extra line-feed
* at the end, typically used by the -showversion in the launcher
*/
public static void println() {
print(System.err);
System.err.println();
public static void println(boolean err) {
print(err, true);
}
/**
* Give a stream, it will print version info on it.
* Print version info.
*/
public static void print(PrintStream ps) {
private static void print(boolean err, boolean newln) {
boolean isHeadless = false;
PrintStream ps = err ? System.err : System.out;
/* Report that we're running headless if the property is true */
String headless = System.getProperty("java.awt.headless");
@ -152,10 +150,14 @@ class VersionProps {
}
/* First line: platform version. */
ps.println(launcher_name + " version \"" + java_version + "\"");
if (err) {
ps.println(launcher_name + " version \"" + java_version + "\"");
} else {
/* Use a format more in line with GNU conventions */
ps.println(launcher_name + " " + java_version);
}
/* Second line: runtime version (ie, libraries). */
String jdk_debug_level = System.getProperty("jdk.debug", "release");
/* Debug level is not printed for "release" builds */
if ("release".equals(jdk_debug_level)) {

@ -37,10 +37,10 @@ package java.lang.annotation;
* <em>type contexts</em> , where annotations apply to types used in
* declarations and expressions.
*
* <p>The constants {@link #ANNOTATION_TYPE} , {@link #CONSTRUCTOR} , {@link
* #FIELD} , {@link #LOCAL_VARIABLE} , {@link #METHOD} , {@link #PACKAGE} ,
* {@link #PARAMETER} , {@link #TYPE} , and {@link #TYPE_PARAMETER} correspond
* to the declaration contexts in JLS 9.6.4.1.
* <p>The constants {@link #ANNOTATION_TYPE}, {@link #CONSTRUCTOR}, {@link
* #FIELD}, {@link #LOCAL_VARIABLE}, {@link #METHOD}, {@link #PACKAGE}, {@link
* #MODULE}, {@link #PARAMETER}, {@link #TYPE}, and {@link #TYPE_PARAMETER}
* correspond to the declaration contexts in JLS 9.6.4.1.
*
* <p>For example, an annotation whose type is meta-annotated with
* {@code @Target(ElementType.FIELD)} may only be written as a modifier for a
@ -107,5 +107,12 @@ public enum ElementType {
*
* @since 1.8
*/
TYPE_USE
TYPE_USE,
/**
* Module declaration.
*
* @since 9
*/
MODULE
}

@ -42,6 +42,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Module;
import java.lang.reflect.ReflectPermission;
import java.nio.ByteOrder;
import java.util.ArrayList;
@ -141,6 +142,59 @@ public class MethodHandles {
}
}
/**
* Returns a {@link Lookup lookup object} with full capabilities to emulate all
* supported bytecode behaviors, including <a href="MethodHandles.Lookup.html#privacc">
* private access</a>, on a target class.
* This method checks that a caller, specified as a {@code Lookup} object, is allowed to
* do <em>deep reflection</em> on the target class. If {@code m1} is the module containing
* the {@link Lookup#lookupClass() lookup class}, and {@code m2} is the module containing
* the target class, then this check ensures that
* <ul>
* <li>{@code m1} {@link Module#canRead reads} {@code m2}.</li>
* <li>{@code m2} {@link Module#isOpen(String,Module) opens} the package containing
* the target class to at least {@code m1}.</li>
* <li>The lookup has the {@link Lookup#MODULE MODULE} lookup mode.</li>
* </ul>
* <p>
* If there is a security manager, its {@code checkPermission} method is called to
* check {@code ReflectPermission("suppressAccessChecks")}.
* @apiNote The {@code MODULE} lookup mode serves to authenticate that the lookup object
* was created by code in the caller module (or derived from a lookup object originally
* created by the caller). A lookup object with the {@code MODULE} lookup mode can be
* shared with trusted parties without giving away {@code PRIVATE} and {@code PACKAGE}
* access to the caller.
* @param targetClass the target class
* @param lookup the caller lookup object
* @return a lookup object for the target class, with private access
* @throws IllegalArgumentException if {@code targetClass} is a primitve type or array class
* @throws NullPointerException if {@code targetClass} or {@code caller} is {@code null}
* @throws IllegalAccessException if the access check specified above fails
* @throws SecurityException if denied by the security manager
* @since 9
*/
public static Lookup privateLookupIn(Class<?> targetClass, Lookup lookup) throws IllegalAccessException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
if (targetClass.isPrimitive())
throw new IllegalArgumentException(targetClass + " is a primitive class");
if (targetClass.isArray())
throw new IllegalArgumentException(targetClass + " is an array class");
Module targetModule = targetClass.getModule();
Module callerModule = lookup.lookupClass().getModule();
if (callerModule != targetModule && targetModule.isNamed()) {
if (!callerModule.canRead(targetModule))
throw new IllegalAccessException(callerModule + " does not read " + targetModule);
String pn = targetClass.getPackageName();
assert pn != null && pn.length() > 0 : "unnamed package cannot be in named module";
if (!targetModule.isOpen(pn, callerModule))
throw new IllegalAccessException(targetModule + " does not open " + pn + " to " + callerModule);
}
if ((lookup.lookupModes() & Lookup.MODULE) == 0)
throw new IllegalAccessException("lookup does not have MODULE lookup mode");
return new Lookup(targetClass);
}
/**
* Performs an unchecked "crack" of a
* <a href="MethodHandleInfo.html#directmh">direct method handle</a>.
@ -1807,7 +1861,12 @@ return mh1;
return callerClass;
}
private boolean hasPrivateAccess() {
/**
* Returns {@code true} if this lookup has {@code PRIVATE} access.
* @return {@code true} if this lookup has {@code PRIVATE} acesss.
* @since 9
*/
public boolean hasPrivateAccess() {
return (allowedModes & PRIVATE) != 0;
}

@ -26,14 +26,20 @@
package java.lang.module;
import java.io.PrintStream;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* The configuration that is the result of resolution or resolution with
@ -46,14 +52,14 @@ import java.util.stream.Collectors;
* dependences expressed by {@code requires} clauses.
*
* The <em>dependence graph</em> is augmented with edges that take account of
* implicitly declared dependences ({@code requires public}) to create a
* implicitly declared dependences ({@code requires transitive}) to create a
* <em>readability graph</em>. A {@code Configuration} encapsulates the
* resulting graph of {@link ResolvedModule resolved modules}.
*
* <p> Suppose we have the following observable modules: </p>
* <pre> {@code
* module m1 { requires m2; }
* module m2 { requires public m3; }
* module m2 { requires transitive m3; }
* module m3 { }
* module m4 { }
* } </pre>
@ -70,8 +76,10 @@ import java.util.stream.Collectors;
* <p> Resolution is an additive process. When computing the transitive closure
* then the dependence relation may include dependences on modules in parent
* configurations. The result is a <em>relative configuration</em> that is
* relative to a parent configuration and where the readability graph may have
* edges from modules in the configuration to modules in a parent configuration.
* relative to one or more parent configurations and where the readability graph
* may have edges from modules in the configuration to modules in parent
* configurations.
*
* </p>
*
* <p> Suppose we have the following observable modules: </p>
@ -96,9 +104,9 @@ import java.util.stream.Collectors;
* <p> {@link ModuleDescriptor#isAutomatic() Automatic} modules receive special
* treatment during resolution. Each automatic module is resolved so that it
* reads all other modules in the configuration and all parent configurations.
* Each automatic module is also resolved as if it {@code requires public} all
* other automatic modules in the configuration (and all automatic modules in
* parent configurations). </p>
* Each automatic module is also resolved as if it {@code requires transitive}
* all other automatic modules in the configuration (and all automatic modules
* in parent configurations). </p>
* <h2><a name="servicebinding">Service binding</a></h2>
*
@ -171,54 +179,157 @@ public final class Configuration {
// @see Configuration#empty()
private static final Configuration EMPTY_CONFIGURATION = new Configuration();
private final Configuration parent;
// parent configurations, in search order
private final List<Configuration> parents;
private final Map<ResolvedModule, Set<ResolvedModule>> graph;
private final Set<ResolvedModule> modules;
private final Map<String, ResolvedModule> nameToModule;
private Configuration() {
this.parent = null;
this.parents = Collections.emptyList();
this.graph = Collections.emptyMap();
this.modules = Collections.emptySet();
this.nameToModule = Collections.emptyMap();
}
private Configuration(Configuration parent,
private Configuration(List<Configuration> parents,
Resolver resolver,
boolean check)
{
Map<ResolvedModule, Set<ResolvedModule>> g = resolver.finish(this, check);
Map<String, ResolvedModule> nameToModule = new HashMap<>();
@SuppressWarnings(value = {"rawtypes", "unchecked"})
Entry<String, ResolvedModule>[] nameEntries
= (Entry<String, ResolvedModule>[])new Entry[g.size()];
ResolvedModule[] moduleArray = new ResolvedModule[g.size()];
int i = 0;
for (ResolvedModule resolvedModule : g.keySet()) {
nameToModule.put(resolvedModule.name(), resolvedModule);
moduleArray[i] = resolvedModule;
nameEntries[i] = Map.entry(resolvedModule.name(), resolvedModule);
i++;
}
this.parent = parent;
this.parents = Collections.unmodifiableList(parents);
this.graph = g;
this.modules = Collections.unmodifiableSet(g.keySet());
this.nameToModule = Collections.unmodifiableMap(nameToModule);
this.modules = Set.of(moduleArray);
this.nameToModule = Map.ofEntries(nameEntries);
}
/**
* Resolves a collection of root modules, with this configuration as its
* parent, to create a new configuration.
* parent, to create a new configuration. This method works exactly as
* specified by the static {@link
* #resolveRequires(ModuleFinder,List,ModuleFinder,Collection) resolveRequires}
* method when invoked with this configuration as the parent. In other words,
* if this configuration is {@code cf} then this method is equivalent to
* invoking:
* <pre> {@code
* Configuration.resolveRequires(before, List.of(cf), after, roots);
* }</pre>
*
* @param before
* The <em>before</em> module finder to find modules
* @param after
* The <em>after</em> module finder to locate modules when a
* module cannot be located by the {@code before} module finder
* and the module is not in this configuration
* @param roots
* The possibly-empty collection of module names of the modules
* to resolve
*
* @return The configuration that is the result of resolving the given
* root modules
*
* @throws ResolutionException
* If resolution or the post-resolution checks fail
* @throws SecurityException
* If locating a module is denied by the security manager
*/
public Configuration resolveRequires(ModuleFinder before,
ModuleFinder after,
Collection<String> roots)
{
return resolveRequires(before, List.of(this), after, roots);
}
/**
* Resolves a collection of root modules, with service binding, and with
* this configuration as its parent, to create a new configuration.
* This method works exactly as specified by the static {@link
* #resolveRequiresAndUses(ModuleFinder,List,ModuleFinder,Collection)
* resolveRequiresAndUses} method when invoked with this configuration
* as the parent. In other words, if this configuration is {@code cf} then
* this method is equivalent to invoking:
* <pre> {@code
* Configuration.resolveRequiresAndUses(before, List.of(cf), after, roots);
* }</pre>
*
*
* @param before
* The <em>before</em> module finder to find modules
* @param after
* The <em>after</em> module finder to locate modules when not
* located by the {@code before} module finder and this
* configuration
* @param roots
* The possibly-empty collection of module names of the modules
* to resolve
*
* @return The configuration that is the result of resolving the given
* root modules
*
* @throws ResolutionException
* If resolution or the post-resolution checks fail
* @throws SecurityException
* If locating a module is denied by the security manager
*/
public Configuration resolveRequiresAndUses(ModuleFinder before,
ModuleFinder after,
Collection<String> roots)
{
return resolveRequiresAndUses(before, List.of(this), after, roots);
}
/**
* Resolves a collection of root modules, with service binding, and with
* the empty configuration as its parent. The post resolution checks
* are optionally run.
*
* This method is used to create the configuration for the boot layer.
*/
static Configuration resolveRequiresAndUses(ModuleFinder finder,
Collection<String> roots,
boolean check,
PrintStream traceOutput)
{
List<Configuration> parents = List.of(empty());
Resolver resolver = new Resolver(finder, parents, ModuleFinder.of(), traceOutput);
resolver.resolveRequires(roots).resolveUses();
return new Configuration(parents, resolver, check);
}
/**
* Resolves a collection of root modules to create a configuration.
*
* <p> Each root module is located using the given {@code before} module
* finder. If a module is not found then it is located in the parent
* configuration as if by invoking the {@link #findModule(String)
* findModule} method. If not found then the module is located using the
* given {@code after} module finder. The same search order is used to
* locate transitive dependences. Root modules or dependences that are
* located in a parent configuration are resolved no further and are not
* included in the resulting configuration. </p>
* findModule} method on each parent in iteration order. If not found then
* the module is located using the given {@code after} module finder. The
* same search order is used to locate transitive dependences. Root modules
* or dependences that are located in a parent configuration are resolved
* no further and are not included in the resulting configuration. </p>
*
* <p> When all modules have been resolved then the resulting dependency
* graph is checked to ensure that it does not contain cycles. A
* readability graph is constructed and then, in conjunction with the
* module exports and service use, checked for consistency. </p>
* readability graph is constructed and in conjunction with the module
* exports and service use, checked for consistency. </p>
*
* <p> Resolution and the (post-resolution) consistency checks may fail for
* following reasons: </p>
@ -262,6 +373,8 @@ public final class Configuration {
*
* @param before
* The <em>before</em> module finder to find modules
* @param parents
* The list parent configurations in search order
* @param after
* The <em>after</em> module finder to locate modules when not
* located by the {@code before} module finder or in parent
@ -274,31 +387,37 @@ public final class Configuration {
* root modules
*
* @throws ResolutionException
* If resolution or the post-resolution checks fail for any of the
* reasons listed
* If resolution or the post-resolution checks fail
* @throws IllegalArgumentException
* If the list of parents is empty
* @throws SecurityException
* If locating a module is denied by the security manager
*/
public Configuration resolveRequires(ModuleFinder before,
ModuleFinder after,
Collection<String> roots)
public static Configuration resolveRequires(ModuleFinder before,
List<Configuration> parents,
ModuleFinder after,
Collection<String> roots)
{
Objects.requireNonNull(before);
Objects.requireNonNull(after);
Objects.requireNonNull(roots);
Resolver resolver = new Resolver(before, this, after, null);
List<Configuration> parentList = new ArrayList<>(parents);
if (parentList.isEmpty())
throw new IllegalArgumentException("'parents' is empty");
Resolver resolver = new Resolver(before, parentList, after, null);
resolver.resolveRequires(roots);
return new Configuration(this, resolver, true);
return new Configuration(parentList, resolver, true);
}
/**
* Resolves a collection of root modules, with service binding, and with
* this configuration as its parent, to create a new configuration.
* Resolves a collection of root modules, with service binding, to create
* configuration.
*
* <p> This method works exactly as specified by {@link #resolveRequires
* <p> This method works exactly as specified by {@link
* #resolveRequires(ModuleFinder,List,ModuleFinder,Collection)
* resolveRequires} except that the graph of resolved modules is augmented
* with modules induced by the service-use dependence relation. </p>
*
@ -319,6 +438,8 @@ public final class Configuration {
*
* @param before
* The <em>before</em> module finder to find modules
* @param parents
* The list parent configurations in search order
* @param after
* The <em>after</em> module finder to locate modules when not
* located by the {@code before} module finder or in parent
@ -331,51 +452,35 @@ public final class Configuration {
* root modules
*
* @throws ResolutionException
* If resolution or the post-resolution checks fail for any of the
* reasons listed
* If resolution or the post-resolution checks fail
* @throws IllegalArgumentException
* If the list of parents is empty
* @throws SecurityException
* If locating a module is denied by the security manager
*/
public Configuration resolveRequiresAndUses(ModuleFinder before,
ModuleFinder after,
Collection<String> roots)
public static Configuration resolveRequiresAndUses(ModuleFinder before,
List<Configuration> parents,
ModuleFinder after,
Collection<String> roots)
{
Objects.requireNonNull(before);
Objects.requireNonNull(after);
Objects.requireNonNull(roots);
Resolver resolver = new Resolver(before, this, after, null);
List<Configuration> parentList = new ArrayList<>(parents);
if (parentList.isEmpty())
throw new IllegalArgumentException("'parents' is empty");
Resolver resolver = new Resolver(before, parentList, after, null);
resolver.resolveRequires(roots).resolveUses();
return new Configuration(this, resolver, true);
return new Configuration(parentList, resolver, true);
}
/**
* Resolves a collection of root modules, with service binding, and with
* the empty configuration as its parent. The post resolution checks
* are optionally run.
*
* This method is used to create the configuration for the boot layer.
*/
static Configuration resolveRequiresAndUses(ModuleFinder finder,
Collection<String> roots,
boolean check,
PrintStream traceOutput)
{
Configuration parent = empty();
Resolver resolver
= new Resolver(finder, parent, ModuleFinder.of(), traceOutput);
resolver.resolveRequires(roots).resolveUses();
return new Configuration(parent, resolver, check);
}
/**
* Returns the <em>empty</em> configuration. The empty configuration does
* not contain any modules and does not have a parent.
* Returns the <em>empty</em> configuration. There are no modules in the
* empty configuration. It has no parents.
*
* @return The empty configuration
*/
@ -385,13 +490,14 @@ public final class Configuration {
/**
* Returns this configuration's parent unless this is the {@linkplain #empty
* empty configuration}, which has no parent.
* Returns an unmodifiable list of this configuration's parents, in search
* order. If this is the {@linkplain #empty empty configuration} then an
* empty list is returned.
*
* @return This configuration's parent
* @return A possibly-empty unmodifiable list of this parent configurations
*/
public Optional<Configuration> parent() {
return Optional.ofNullable(parent);
public List<Configuration> parents() {
return parents;
}
@ -408,23 +514,35 @@ public final class Configuration {
/**
* Finds a resolved module in this configuration, or if not in this
* configuration, the {@linkplain #parent parent} configurations.
* configuration, the {@linkplain #parents parent} configurations.
* Finding a module in parent configurations is equivalent to invoking
* {@code findModule} on each parent, in search order, until the module
* is found or all parents have been searched. In a <em>tree of
* configurations</em> then this is equivalent to a depth-first search.
*
* @param name
* The module name of the resolved module to find
*
* @return The resolved module with the given name or an empty {@code
* Optional} if there isn't a module with this name in this
* configuration or any parent configuration
* configuration or any parent configurations
*/
public Optional<ResolvedModule> findModule(String name) {
Objects.requireNonNull(name);
if (parent == null)
return Optional.empty();
ResolvedModule m = nameToModule.get(name);
if (m != null)
return Optional.of(m);
return parent().flatMap(x -> x.findModule(name));
if (!parents.isEmpty()) {
return configurations()
.skip(1) // skip this configuration
.map(cf -> cf.nameToModule)
.filter(map -> map.containsKey(name))
.map(map -> map.get(name))
.findFirst();
}
return Optional.empty();
}
@ -443,10 +561,47 @@ public final class Configuration {
return Collections.unmodifiableSet(graph.get(m));
}
/**
* Returns an ordered stream of configurations. The first element is this
* configuration, the remaining elements are the parent configurations
* in DFS order.
*
* @implNote For now, the assumption is that the number of elements will
* be very low and so this method does not use a specialized spliterator.
*/
Stream<Configuration> configurations() {
List<Configuration> allConfigurations = this.allConfigurations;
if (allConfigurations == null) {
allConfigurations = new ArrayList<>();
Set<Configuration> visited = new HashSet<>();
Deque<Configuration> stack = new ArrayDeque<>();
visited.add(this);
stack.push(this);
while (!stack.isEmpty()) {
Configuration layer = stack.pop();
allConfigurations.add(layer);
// push in reverse order
for (int i = layer.parents.size() - 1; i >= 0; i--) {
Configuration parent = layer.parents.get(i);
if (!visited.contains(parent)) {
visited.add(parent);
stack.push(parent);
}
}
}
this.allConfigurations = Collections.unmodifiableList(allConfigurations);
}
return allConfigurations.stream();
}
private volatile List<Configuration> allConfigurations;
/**
* Returns a string describing this configuration.
*
* @return A string describing this configuration
* @return A possibly empty string describing this configuration
*/
@Override
public String toString() {

File diff suppressed because it is too large Load Diff

@ -233,10 +233,11 @@ public interface ModuleFinder {
* ModuleDescriptor.Version} and ignored if it cannot be parsed as
* a {@code Version}. </p></li>
*
* <li><p> For the module name, then all non-alphanumeric
* characters ({@code [^A-Za-z0-9])} are replaced with a dot
* ({@code "."}), all repeating dots are replaced with one dot,
* and all leading and trailing dots are removed. </p></li>
* <li><p> For the module name, then any trailing digits and dots
* are removed, all non-alphanumeric characters ({@code [^A-Za-z0-9]})
* are replaced with a dot ({@code "."}), all repeating dots are
* replaced with one dot, and all leading and trailing dots are
* removed. </p></li>
*
* <li><p> As an example, a JAR file named {@code foo-bar.jar} will
* derive a module name {@code foo.bar} and no version. A JAR file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2016, 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
@ -31,13 +31,17 @@ import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.module.ModuleDescriptor.Requires.Modifier;
import java.lang.module.ModuleDescriptor.Builder;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.nio.ByteBuffer;
import java.nio.BufferUnderflowException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
@ -56,15 +60,12 @@ import static jdk.internal.module.ClassFileConstants.*;
final class ModuleInfo {
// supplies the set of packages when ConcealedPackages not present
// supplies the set of packages when ModulePackages attribute not present
private final Supplier<Set<String>> packageFinder;
// indicates if the Hashes attribute should be parsed
// indicates if the ModuleHashes attribute should be parsed
private final boolean parseHashes;
// the builder, created when parsing
private ModuleDescriptor.Builder builder;
private ModuleInfo(Supplier<Set<String>> pf, boolean ph) {
packageFinder = pf;
parseHashes = ph;
@ -86,9 +87,8 @@ final class ModuleInfo {
{
try {
return new ModuleInfo(pf).doRead(new DataInputStream(in));
} catch (IllegalArgumentException iae) {
// IllegalArgumentException means a malformed class
throw invalidModuleDescriptor(iae.getMessage());
} catch (IllegalArgumentException | IllegalStateException e) {
throw invalidModuleDescriptor(e.getMessage());
} catch (EOFException x) {
throw truncatedModuleDescriptor();
}
@ -105,9 +105,8 @@ final class ModuleInfo {
{
try {
return new ModuleInfo(pf).doRead(new DataInputWrapper(bb));
} catch (IllegalArgumentException iae) {
// IllegalArgumentException means a malformed class
throw invalidModuleDescriptor(iae.getMessage());
} catch (IllegalArgumentException | IllegalStateException e) {
throw invalidModuleDescriptor(e.getMessage());
} catch (EOFException x) {
throw truncatedModuleDescriptor();
} catch (IOException ioe) {
@ -117,7 +116,7 @@ final class ModuleInfo {
/**
* Reads a {@code module-info.class} from the given byte buffer
* but ignore the {@code Hashes} attribute.
* but ignore the {@code ModuleHashes} attribute.
*
* @throws InvalidModuleDescriptorException
* @throws UncheckedIOException
@ -127,8 +126,8 @@ final class ModuleInfo {
{
try {
return new ModuleInfo(pf, false).doRead(new DataInputWrapper(bb));
} catch (IllegalArgumentException iae) {
throw invalidModuleDescriptor(iae.getMessage());
} catch (IllegalArgumentException | IllegalStateException e) {
throw invalidModuleDescriptor(e.getMessage());
} catch (EOFException x) {
throw truncatedModuleDescriptor();
} catch (IOException ioe) {
@ -164,12 +163,8 @@ final class ModuleInfo {
throw invalidModuleDescriptor("access_flags should be ACC_MODULE");
int this_class = in.readUnsignedShort();
String mn = cpool.getClassName(this_class);
int suffix = mn.indexOf("/module-info");
if (suffix < 1)
throw invalidModuleDescriptor("this_class not of form name/module-info");
mn = mn.substring(0, suffix).replace('/', '.');
builder = new ModuleDescriptor.Builder(mn);
if (this_class != 0)
throw invalidModuleDescriptor("this_class must be 0");
int super_class = in.readUnsignedShort();
if (super_class > 0)
@ -192,6 +187,13 @@ final class ModuleInfo {
// the names of the attributes found in the class file
Set<String> attributes = new HashSet<>();
Builder builder = null;
Set<String> packages = null;
String version = null;
String mainClass = null;
String[] osValues = null;
ModuleHashes hashes = null;
for (int i = 0; i < attributes_count ; i++) {
int name_index = in.readUnsignedShort();
String attribute_name = cpool.getUtf8(name_index);
@ -206,28 +208,28 @@ final class ModuleInfo {
switch (attribute_name) {
case MODULE :
readModuleAttribute(mn, in, cpool);
builder = readModuleAttribute(in, cpool);
break;
case CONCEALED_PACKAGES :
readConcealedPackagesAttribute(in, cpool);
case MODULE_PACKAGES :
packages = readModulePackagesAttribute(in, cpool);
break;
case VERSION :
readVersionAttribute(in, cpool);
case MODULE_VERSION :
version = readModuleVersionAttribute(in, cpool);
break;
case MAIN_CLASS :
readMainClassAttribute(in, cpool);
case MODULE_MAIN_CLASS :
mainClass = readModuleMainClassAttribute(in, cpool);
break;
case TARGET_PLATFORM :
readTargetPlatformAttribute(in, cpool);
case MODULE_TARGET :
osValues = readModuleTargetAttribute(in, cpool);
break;
case HASHES :
case MODULE_HASHES :
if (parseHashes) {
readHashesAttribute(in, cpool);
hashes = readModuleHashesAttribute(in, cpool);
} else {
in.skipBytes(length);
}
@ -245,53 +247,91 @@ final class ModuleInfo {
}
// the Module attribute is required
if (!attributes.contains(MODULE)) {
if (builder == null) {
throw invalidModuleDescriptor(MODULE + " attribute not found");
}
// If the ConcealedPackages attribute is not present then the
// packageFinder is used to to find any non-exported packages.
if (!attributes.contains(CONCEALED_PACKAGES) && packageFinder != null) {
Set<String> pkgs;
// If the ModulePackages attribute is not present then the packageFinder
// is used to find the set of packages
boolean usedPackageFinder = false;
if (packages == null && packageFinder != null) {
try {
pkgs = new HashSet<>(packageFinder.get());
packages = new HashSet<>(packageFinder.get());
} catch (UncheckedIOException x) {
throw x.getCause();
}
pkgs.removeAll(builder.exportedPackages());
builder.conceals(pkgs);
usedPackageFinder = true;
}
if (packages != null) {
for (String pn : builder.exportedAndOpenPackages()) {
if (!packages.contains(pn)) {
String tail;
if (usedPackageFinder) {
tail = " not found by package finder";
} else {
tail = " missing from ModulePackages attribute";
}
throw invalidModuleDescriptor("Package " + pn + tail);
}
packages.remove(pn);
}
builder.contains(packages);
}
// Was the Synthetic attribute present?
if (attributes.contains(SYNTHETIC))
builder.synthetic(true);
if (version != null)
builder.version(version);
if (mainClass != null)
builder.mainClass(mainClass);
if (osValues != null) {
if (osValues[0] != null) builder.osName(osValues[0]);
if (osValues[1] != null) builder.osArch(osValues[1]);
if (osValues[2] != null) builder.osVersion(osValues[2]);
}
if (hashes != null)
builder.hashes(hashes);
return builder.build();
}
/**
* Reads the Module attribute.
* Reads the Module attribute, returning the ModuleDescriptor.Builder to
* build the corresponding ModuleDescriptor.
*/
private void readModuleAttribute(String mn, DataInput in, ConstantPool cpool)
private Builder readModuleAttribute(DataInput in, ConstantPool cpool)
throws IOException
{
// module_name
int module_name_index = in.readUnsignedShort();
String mn = cpool.getUtf8AsBinaryName(module_name_index);
Builder builder = new ModuleDescriptor.Builder(mn, /*strict*/ false);
int module_flags = in.readUnsignedShort();
boolean open = ((module_flags & ACC_OPEN) != 0);
if (open)
builder.open(true);
if ((module_flags & ACC_SYNTHETIC) != 0)
builder.synthetic(true);
int requires_count = in.readUnsignedShort();
boolean requiresJavaBase = false;
for (int i=0; i<requires_count; i++) {
int index = in.readUnsignedShort();
int flags = in.readUnsignedShort();
String dn = cpool.getUtf8(index);
Set<Modifier> mods;
String dn = cpool.getUtf8AsBinaryName(index);
Set<Requires.Modifier> mods;
if (flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_PUBLIC) != 0)
mods.add(Modifier.PUBLIC);
if ((flags & ACC_TRANSITIVE) != 0)
mods.add(Requires.Modifier.TRANSITIVE);
if ((flags & ACC_STATIC_PHASE) != 0)
mods.add(Requires.Modifier.STATIC);
if ((flags & ACC_SYNTHETIC) != 0)
mods.add(Modifier.SYNTHETIC);
mods.add(Requires.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
mods.add(Modifier.MANDATED);
mods.add(Requires.Modifier.MANDATED);
}
builder.requires(mods, dn);
if (dn.equals("java.base"))
@ -311,17 +351,66 @@ final class ModuleInfo {
if (exports_count > 0) {
for (int i=0; i<exports_count; i++) {
int index = in.readUnsignedShort();
String pkg = cpool.getUtf8(index).replace('/', '.');
String pkg = cpool.getUtf8AsBinaryName(index);
Set<Exports.Modifier> mods;
int flags = in.readUnsignedShort();
if (flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_SYNTHETIC) != 0)
mods.add(Exports.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
mods.add(Exports.Modifier.MANDATED);
}
int exports_to_count = in.readUnsignedShort();
if (exports_to_count > 0) {
Set<String> targets = new HashSet<>(exports_to_count);
for (int j=0; j<exports_to_count; j++) {
int exports_to_index = in.readUnsignedShort();
targets.add(cpool.getUtf8(exports_to_index));
targets.add(cpool.getUtf8AsBinaryName(exports_to_index));
}
builder.exports(pkg, targets);
builder.exports(mods, pkg, targets);
} else {
builder.exports(pkg);
builder.exports(mods, pkg);
}
}
}
int opens_count = in.readUnsignedShort();
if (opens_count > 0) {
if (open) {
throw invalidModuleDescriptor("The opens table for an open"
+ " module must be 0 length");
}
for (int i=0; i<opens_count; i++) {
int index = in.readUnsignedShort();
String pkg = cpool.getUtf8AsBinaryName(index);
Set<Opens.Modifier> mods;
int flags = in.readUnsignedShort();
if (flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_SYNTHETIC) != 0)
mods.add(Opens.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
mods.add(Opens.Modifier.MANDATED);
}
int open_to_count = in.readUnsignedShort();
if (open_to_count > 0) {
Set<String> targets = new HashSet<>(open_to_count);
for (int j=0; j<open_to_count; j++) {
int opens_to_index = in.readUnsignedShort();
targets.add(cpool.getUtf8AsBinaryName(opens_to_index));
}
builder.opens(mods, pkg, targets);
} else {
builder.opens(mods, pkg);
}
}
}
@ -330,111 +419,114 @@ final class ModuleInfo {
if (uses_count > 0) {
for (int i=0; i<uses_count; i++) {
int index = in.readUnsignedShort();
String sn = cpool.getClassName(index).replace('/', '.');
String sn = cpool.getClassNameAsBinaryName(index);
builder.uses(sn);
}
}
int provides_count = in.readUnsignedShort();
if (provides_count > 0) {
Map<String, Set<String>> pm = new HashMap<>();
for (int i=0; i<provides_count; i++) {
int index = in.readUnsignedShort();
int with_index = in.readUnsignedShort();
String sn = cpool.getClassName(index).replace('/', '.');
String cn = cpool.getClassName(with_index).replace('/', '.');
// computeIfAbsent
Set<String> providers = pm.get(sn);
if (providers == null) {
providers = new LinkedHashSet<>(); // preserve order
pm.put(sn, providers);
String sn = cpool.getClassNameAsBinaryName(index);
int with_count = in.readUnsignedShort();
List<String> providers = new ArrayList<>(with_count);
for (int j=0; j<with_count; j++) {
index = in.readUnsignedShort();
String pn = cpool.getClassNameAsBinaryName(index);
providers.add(pn);
}
providers.add(cn);
}
for (Map.Entry<String, Set<String>> e : pm.entrySet()) {
builder.provides(e.getKey(), e.getValue());
builder.provides(sn, providers);
}
}
return builder;
}
/**
* Reads the ConcealedPackages attribute
* Reads the ModulePackages attribute
*/
private void readConcealedPackagesAttribute(DataInput in, ConstantPool cpool)
private Set<String> readModulePackagesAttribute(DataInput in, ConstantPool cpool)
throws IOException
{
int package_count = in.readUnsignedShort();
Set<String> packages = new HashSet<>(package_count);
for (int i=0; i<package_count; i++) {
int index = in.readUnsignedShort();
String pn = cpool.getUtf8(index).replace('/', '.');
builder.conceals(pn);
String pn = cpool.getUtf8AsBinaryName(index);
packages.add(pn);
}
return packages;
}
/**
* Reads the Version attribute
* Reads the ModuleVersion attribute
*/
private void readVersionAttribute(DataInput in, ConstantPool cpool)
private String readModuleVersionAttribute(DataInput in, ConstantPool cpool)
throws IOException
{
int index = in.readUnsignedShort();
builder.version(cpool.getUtf8(index));
return cpool.getUtf8(index);
}
/**
* Reads the MainClass attribute
* Reads the ModuleMainClass attribute
*/
private void readMainClassAttribute(DataInput in, ConstantPool cpool)
private String readModuleMainClassAttribute(DataInput in, ConstantPool cpool)
throws IOException
{
int index = in.readUnsignedShort();
builder.mainClass(cpool.getClassName(index).replace('/', '.'));
return cpool.getClassNameAsBinaryName(index);
}
/**
* Reads the TargetPlatform attribute
* Reads the ModuleTarget attribute
*/
private void readTargetPlatformAttribute(DataInput in, ConstantPool cpool)
private String[] readModuleTargetAttribute(DataInput in, ConstantPool cpool)
throws IOException
{
String[] values = new String[3];
int name_index = in.readUnsignedShort();
if (name_index != 0)
builder.osName(cpool.getUtf8(name_index));
values[0] = cpool.getUtf8(name_index);
int arch_index = in.readUnsignedShort();
if (arch_index != 0)
builder.osArch(cpool.getUtf8(arch_index));
values[1] = cpool.getUtf8(arch_index);
int version_index = in.readUnsignedShort();
if (version_index != 0)
builder.osVersion(cpool.getUtf8(version_index));
values[2] = cpool.getUtf8(version_index);
return values;
}
/**
* Reads the Hashes attribute
*
* @apiNote For now the hash is stored in base64 as a UTF-8 string, this
* should be changed to be an array of u1.
* Reads the ModuleHashes attribute
*/
private void readHashesAttribute(DataInput in, ConstantPool cpool)
private ModuleHashes readModuleHashesAttribute(DataInput in, ConstantPool cpool)
throws IOException
{
int index = in.readUnsignedShort();
String algorithm = cpool.getUtf8(index);
int algorithm_index = in.readUnsignedShort();
String algorithm = cpool.getUtf8(algorithm_index);
int hash_count = in.readUnsignedShort();
Map<String, String> map = new HashMap<>(hash_count);
Map<String, byte[]> map = new HashMap<>(hash_count);
for (int i=0; i<hash_count; i++) {
index = in.readUnsignedShort();
String dn = cpool.getUtf8(index);
index = in.readUnsignedShort();
String hash = cpool.getUtf8(index);
map.put(dn, hash);
int module_name_index = in.readUnsignedShort();
String mn = cpool.getUtf8AsBinaryName(module_name_index);
int hash_length = in.readUnsignedShort();
if (hash_length == 0) {
throw invalidModuleDescriptor("hash_length == 0");
}
byte[] hash = new byte[hash_length];
in.readFully(hash);
map.put(mn, hash);
}
builder.hashes(new ModuleHashes(algorithm, map));
return new ModuleHashes(algorithm, map);
}
@ -447,11 +539,11 @@ final class ModuleInfo {
if (name.equals(MODULE) ||
name.equals(SOURCE_FILE) ||
name.equals(SDE) ||
name.equals(CONCEALED_PACKAGES) ||
name.equals(VERSION) ||
name.equals(MAIN_CLASS) ||
name.equals(TARGET_PLATFORM) ||
name.equals(HASHES))
name.equals(MODULE_PACKAGES) ||
name.equals(MODULE_VERSION) ||
name.equals(MODULE_MAIN_CLASS) ||
name.equals(MODULE_TARGET) ||
name.equals(MODULE_HASHES))
return true;
return false;
@ -461,8 +553,8 @@ final class ModuleInfo {
* Return true if the given attribute name is the name of a pre-defined
* attribute that is not allowed in the class file.
*
* Except for Module, InnerClasses, Synthetic, SourceFile, SourceDebugExtension,
* and Deprecated, none of the pre-defined attributes in JVMS 4.7 may appear.
* Except for Module, InnerClasses, SourceFile, SourceDebugExtension, and
* Deprecated, none of the pre-defined attributes in JVMS 4.7 may appear.
*/
private static boolean isAttributeDisallowed(String name) {
Set<String> notAllowed = predefinedNotAllowed;
@ -477,12 +569,11 @@ final class ModuleInfo {
"LineNumberTable",
"LocalVariableTable",
"LocalVariableTypeTable",
"RuntimeVisibleAnnotations",
"RuntimeInvisibleAnnotations",
"RuntimeVisibleParameterAnnotations",
"RuntimeInvisibleParameterAnnotations",
"RuntimeVisibleTypeAnnotations",
"RuntimeInvisibleTypeAnnotations",
"Synthetic",
"AnnotationDefault",
"BootstrapMethods",
"MethodParameters");
@ -495,7 +586,6 @@ final class ModuleInfo {
private static volatile Set<String> predefinedNotAllowed;
/**
* The constant pool in a class file.
*/
@ -628,6 +718,11 @@ final class ModuleInfo {
return getUtf8(((IndexEntry) e).index);
}
String getClassNameAsBinaryName(int index) {
String value = getClassName(index);
return value.replace('/', '.'); // internal form -> binary name
}
String getUtf8(int index) {
checkIndex(index);
Entry e = pool[index];
@ -638,6 +733,11 @@ final class ModuleInfo {
return (String) (((ValueEntry) e).value);
}
String getUtf8AsBinaryName(int index) {
String value = getUtf8(index);
return value.replace('/', '.'); // internal -> binary name
}
void checkIndex(int index) {
if (index < 1 || index >= pool.length)
throw invalidModuleDescriptor("Index into constant pool out of range");

@ -40,9 +40,10 @@ import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@ -54,7 +55,6 @@ import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import jdk.internal.jmod.JmodFile;
@ -399,8 +399,8 @@ class ModulePath implements ModuleFinder {
*
* 1. The module name (and optionally the version) is derived from the file
* name of the JAR file
* 2. The packages of all .class files in the JAR file are exported
* 3. It has no module-private/concealed packages
* 2. All packages are exported and open
* 3. It has no non-exported/non-open packages
* 4. The contents of any META-INF/services configuration files are mapped
* to "provides" declarations
* 5. The Main-Class attribute in the main attributes of the JAR manifest
@ -440,8 +440,7 @@ class ModulePath implements ModuleFinder {
// Builder throws IAE if module name is empty or invalid
ModuleDescriptor.Builder builder
= new ModuleDescriptor.Builder(mn)
.automatic()
= ModuleDescriptor.automaticModule(mn)
.requires(Set.of(Requires.Modifier.MANDATED), "java.base");
if (vs != null)
builder.version(vs);
@ -455,13 +454,12 @@ class ModulePath implements ModuleFinder {
Set<String> resources = map.get(Boolean.FALSE);
Set<String> configFiles = map.get(Boolean.TRUE);
// all packages are exported
// all packages are exported and open
resources.stream()
.map(this::toPackageName)
.flatMap(Optional::stream)
.distinct()
.forEach(builder::exports);
.forEach(pn -> builder.exports(pn).opens(pn));
// map names of service configuration files to service names
Set<String> serviceNames = configFiles.stream()
@ -472,7 +470,7 @@ class ModulePath implements ModuleFinder {
// parse each service configuration file
for (String sn : serviceNames) {
JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
Set<String> providerClasses = new LinkedHashSet<>();
List<String> providerClasses = new ArrayList<>();
try (InputStream in = jf.getInputStream(entry)) {
BufferedReader reader
= new BufferedReader(new InputStreamReader(in, "UTF-8"));
@ -493,7 +491,7 @@ class ModulePath implements ModuleFinder {
Attributes attrs = man.getMainAttributes();
String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS);
if (mainClass != null)
builder.mainClass(mainClass);
builder.mainClass(mainClass.replace("/", "."));
}
return builder.build();
@ -504,6 +502,7 @@ class ModulePath implements ModuleFinder {
*/
private static class Patterns {
static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))");
static final Pattern TRAILING_VERSION = Pattern.compile("(\\.|\\d)*$");
static final Pattern NON_ALPHANUM = Pattern.compile("[^A-Za-z0-9]");
static final Pattern REPEATING_DOTS = Pattern.compile("(\\.)(\\1)+");
static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
@ -514,6 +513,9 @@ class ModulePath implements ModuleFinder {
* Clean up candidate module name derived from a JAR file name.
*/
private static String cleanModuleName(String mn) {
// drop trailing version from name
mn = Patterns.TRAILING_VERSION.matcher(mn).replaceAll("");
// replace non-alphanumeric
mn = Patterns.NON_ALPHANUM.matcher(mn).replaceAll(".");

@ -60,8 +60,8 @@ public final class ModuleReference {
// the function that computes the hash of this module reference
private final HashSupplier hasher;
// cached hash string to avoid needing to compute it many times
private String cachedHash;
// cached hash to avoid needing to compute it many times
private byte[] cachedHash;
/**
@ -183,13 +183,13 @@ public final class ModuleReference {
}
/**
* Computes the hash of this module, returning it as a hex string.
* Returns {@code null} if the hash cannot be computed.
* Computes the hash of this module. Returns {@code null} if the hash
* cannot be computed.
*
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
String computeHash(String algorithm) {
String result = cachedHash;
byte[] computeHash(String algorithm) {
byte[] result = cachedHash;
if (result != null)
return result;
if (hasher == null)
@ -211,8 +211,11 @@ public final class ModuleReference {
public int hashCode() {
int hc = hash;
if (hc == 0) {
hc = Objects.hash(descriptor, location, readerSupplier, hasher,
Boolean.valueOf(patched));
hc = descriptor.hashCode();
hc = 43 * hc + readerSupplier.hashCode();
hc = 43 * hc + Objects.hashCode(location);
hc = 43 * hc + Objects.hashCode(hasher);
hc = 43 * hc + Boolean.hashCode(patched);
if (hc == 0)
hc = -1;
hash = hc;

@ -51,6 +51,7 @@ import java.util.zip.ZipFile;
import jdk.internal.jmod.JmodFile;
import jdk.internal.misc.JavaLangAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.ModuleBootstrap;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleHashes.HashSupplier;
import jdk.internal.module.ModulePatcher;
@ -80,9 +81,8 @@ class ModuleReferences {
HashSupplier hasher) {
ModuleReference mref = new ModuleReference(md, uri, supplier, hasher);
if (JLA.getBootLayer() == null)
mref = ModulePatcher.interposeIfNeeded(mref);
mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
return mref;
}
@ -93,7 +93,7 @@ class ModuleReferences {
static ModuleReference newJarModule(ModuleDescriptor md, Path file) {
URI uri = file.toUri();
Supplier<ModuleReader> supplier = () -> new JarModuleReader(file, uri);
HashSupplier hasher = (a) -> ModuleHashes.computeHashAsString(file, a);
HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a);
return newModule(md, uri, supplier, hasher);
}
@ -103,7 +103,7 @@ class ModuleReferences {
static ModuleReference newJModModule(ModuleDescriptor md, Path file) {
URI uri = file.toUri();
Supplier<ModuleReader> supplier = () -> new JModModuleReader(file, uri);
HashSupplier hasher = (a) -> ModuleHashes.computeHashAsString(file, a);
HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a);
return newModule(md, file.toUri(), supplier, hasher);
}

@ -26,9 +26,12 @@
package java.lang.module;
import java.io.PrintStream;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Requires.Modifier;
import java.lang.reflect.Layer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
@ -47,12 +50,15 @@ import jdk.internal.module.ModuleHashes;
/**
* The resolver used by {@link Configuration#resolveRequires} and
* {@link Configuration#resolveRequiresAndUses}.
*
* @implNote The resolver is used at VM startup and so deliberately avoids
* using lambda and stream usages in code paths used during startup.
*/
final class Resolver {
private final ModuleFinder beforeFinder;
private final Configuration parent;
private final List<Configuration> parents;
private final ModuleFinder afterFinder;
private final PrintStream traceOutput;
@ -61,11 +67,11 @@ final class Resolver {
Resolver(ModuleFinder beforeFinder,
Configuration parent,
List<Configuration> parents,
ModuleFinder afterFinder,
PrintStream traceOutput) {
this.beforeFinder = beforeFinder;
this.parent = parent;
this.parents = parents;
this.afterFinder = afterFinder;
this.traceOutput = traceOutput;
}
@ -85,10 +91,12 @@ final class Resolver {
// find root module
ModuleReference mref = findWithBeforeFinder(root);
if (mref == null) {
if (parent.findModule(root).isPresent()) {
if (findInParent(root) != null) {
// in parent, nothing to do
continue;
}
mref = findWithAfterFinder(root);
if (mref == null) {
fail("Module %s not found", root);
@ -125,13 +133,21 @@ final class Resolver {
// process dependences
for (ModuleDescriptor.Requires requires : descriptor.requires()) {
// only required at compile-time
if (requires.modifiers().contains(Modifier.STATIC))
continue;
String dn = requires.name();
// find dependence
ModuleReference mref = findWithBeforeFinder(dn);
if (mref == null) {
if (parent.findModule(dn).isPresent())
if (findInParent(dn) != null) {
// dependence is in parent
continue;
}
mref = findWithAfterFinder(dn);
if (mref == null) {
@ -174,7 +190,9 @@ final class Resolver {
ModuleDescriptor descriptor = mref.descriptor();
if (!descriptor.provides().isEmpty()) {
for (String sn : descriptor.provides().keySet()) {
for (Provides provides : descriptor.provides()) {
String sn = provides.service();
// computeIfAbsent
Set<ModuleReference> providers = availableProviders.get(sn);
if (providers == null) {
@ -191,19 +209,23 @@ final class Resolver {
Deque<ModuleDescriptor> q = new ArrayDeque<>();
// the initial set of modules that may use services
Set<ModuleDescriptor> candidateConsumers = new HashSet<>();
Configuration p = parent;
while (p != null) {
candidateConsumers.addAll(p.descriptors());
p = p.parent().orElse(null);
Set<ModuleDescriptor> initialConsumers;
if (Layer.boot() == null) {
initialConsumers = new HashSet<>();
} else {
initialConsumers = parents.stream()
.flatMap(Configuration::configurations)
.distinct()
.flatMap(c -> c.descriptors().stream())
.collect(Collectors.toSet());
}
for (ModuleReference mref : nameToReference.values()) {
candidateConsumers.add(mref.descriptor());
initialConsumers.add(mref.descriptor());
}
// Where there is a consumer of a service then resolve all modules
// that provide an implementation of that service
Set<ModuleDescriptor> candidateConsumers = initialConsumers;
do {
for (ModuleDescriptor descriptor : candidateConsumers) {
if (!descriptor.uses().isEmpty()) {
@ -234,7 +256,6 @@ final class Resolver {
}
candidateConsumers = resolve(q);
} while (!candidateConsumers.isEmpty());
return this;
@ -429,21 +450,21 @@ final class Resolver {
for (String dn : hashes.names()) {
ModuleReference other = nameToReference.get(dn);
if (other == null) {
other = parent.findModule(dn)
.map(ResolvedModule::reference)
.orElse(null);
ResolvedModule resolvedModule = findInParent(dn);
if (resolvedModule != null)
other = resolvedModule.reference();
}
// skip checking the hash if the module has been patched
if (other != null && !other.isPatched()) {
String recordedHash = hashes.hashFor(dn);
String actualHash = other.computeHash(algorithm);
byte[] recordedHash = hashes.hashFor(dn);
byte[] actualHash = other.computeHash(algorithm);
if (actualHash == null)
fail("Unable to compute the hash of module %s", dn);
if (!recordedHash.equals(actualHash)) {
if (!Arrays.equals(recordedHash, actualHash)) {
fail("Hash of %s (%s) differs to expected hash (%s)" +
" recorded in %s", dn, actualHash, recordedHash,
descriptor.name());
" recorded in %s", dn, toHexString(actualHash),
toHexString(recordedHash), descriptor.name());
}
}
}
@ -451,52 +472,68 @@ final class Resolver {
}
}
private static String toHexString(byte[] ba) {
StringBuilder sb = new StringBuilder(ba.length * 2);
for (byte b: ba) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
}
/**
* Computes the readability graph for the modules in the given Configuration.
*
* The readability graph is created by propagating "requires" through the
* "public requires" edges of the module dependence graph. So if the module
* dependence graph has m1 requires m2 && m2 requires public m3 then the
* resulting readability graph will contain m1 reads m2, m1 reads m3, and
* m2 reads m3.
* "requires transitive" edges of the module dependence graph. So if the
* module dependence graph has m1 requires m2 && m2 requires transitive m3
* then the resulting readability graph will contain m1 reads m2, m1 reads m3,
* and m2 reads m3.
*/
private Map<ResolvedModule, Set<ResolvedModule>> makeGraph(Configuration cf) {
// initial capacity of maps to avoid resizing
int capacity = 1 + (4 * nameToReference.size())/ 3;
// the "reads" graph starts as a module dependence graph and
// is iteratively updated to be the readability graph
Map<ResolvedModule, Set<ResolvedModule>> g1 = new HashMap<>();
Map<ResolvedModule, Set<ResolvedModule>> g1 = new HashMap<>(capacity);
// the "requires public" graph, contains requires public edges only
Map<ResolvedModule, Set<ResolvedModule>> g2 = new HashMap<>();
// the "requires transitive" graph, contains requires transitive edges only
Map<ResolvedModule, Set<ResolvedModule>> g2;
// need "requires public" from the modules in parent configurations as
// there may be selected modules that have a dependency on modules in
// need "requires transitive" from the modules in parent configurations
// as there may be selected modules that have a dependency on modules in
// the parent configuration.
Configuration p = parent;
while (p != null) {
for (ModuleDescriptor descriptor : p.descriptors()) {
String name = descriptor.name();
ResolvedModule m1 = p.findModule(name)
.orElseThrow(() -> new InternalError(name + " not found"));
for (ModuleDescriptor.Requires requires : descriptor.requires()) {
if (requires.modifiers().contains(Modifier.PUBLIC)) {
String dn = requires.name();
ResolvedModule m2 = p.findModule(dn)
.orElseThrow(() -> new InternalError(dn + " not found"));
g2.computeIfAbsent(m1, k -> new HashSet<>()).add(m2);
}
}
}
p = p.parent().orElse(null);
if (Layer.boot() == null) {
g2 = new HashMap<>(capacity);
} else {
g2 = parents.stream()
.flatMap(Configuration::configurations)
.distinct()
.flatMap(c ->
c.modules().stream().flatMap(m1 ->
m1.descriptor().requires().stream()
.filter(r -> r.modifiers().contains(Modifier.TRANSITIVE))
.flatMap(r -> {
Optional<ResolvedModule> m2 = c.findModule(r.name());
assert m2.isPresent()
|| r.modifiers().contains(Modifier.STATIC);
return m2.stream();
})
.map(m2 -> Map.entry(m1, m2))
)
)
// stream of m1->m2
.collect(Collectors.groupingBy(Map.Entry::getKey,
HashMap::new,
Collectors.mapping(Map.Entry::getValue, Collectors.toSet())
));
}
// populate g1 and g2 with the dependences from the selected modules
Map<String, ResolvedModule> nameToResolved = new HashMap<>();
Map<String, ResolvedModule> nameToResolved = new HashMap<>(capacity);
for (ModuleReference mref : nameToReference.values()) {
ModuleDescriptor descriptor = mref.descriptor();
@ -505,20 +542,21 @@ final class Resolver {
ResolvedModule m1 = computeIfAbsent(nameToResolved, name, cf, mref);
Set<ResolvedModule> reads = new HashSet<>();
Set<ResolvedModule> requiresPublic = new HashSet<>();
Set<ResolvedModule> requiresTransitive = new HashSet<>();
for (ModuleDescriptor.Requires requires : descriptor.requires()) {
String dn = requires.name();
ResolvedModule m2;
ResolvedModule m2 = null;
ModuleReference mref2 = nameToReference.get(dn);
if (mref2 != null) {
// same configuration
m2 = computeIfAbsent(nameToResolved, dn, cf, mref2);
} else {
// parent configuration
m2 = parent.findModule(dn).orElse(null);
m2 = findInParent(dn);
if (m2 == null) {
assert requires.modifiers().contains(Modifier.STATIC);
continue;
}
}
@ -526,9 +564,9 @@ final class Resolver {
// m1 requires m2 => m1 reads m2
reads.add(m2);
// m1 requires public m2
if (requires.modifiers().contains(Modifier.PUBLIC)) {
requiresPublic.add(m2);
// m1 requires transitive m2
if (requires.modifiers().contains(Modifier.TRANSITIVE)) {
requiresTransitive.add(m2);
}
}
@ -538,7 +576,7 @@ final class Resolver {
if (descriptor.isAutomatic()) {
// reads all selected modules
// `requires public` all selected automatic modules
// `requires transitive` all selected automatic modules
for (ModuleReference mref2 : nameToReference.values()) {
ModuleDescriptor descriptor2 = mref2.descriptor();
String name2 = descriptor2.name();
@ -548,40 +586,42 @@ final class Resolver {
= computeIfAbsent(nameToResolved, name2, cf, mref2);
reads.add(m2);
if (descriptor2.isAutomatic())
requiresPublic.add(m2);
requiresTransitive.add(m2);
}
}
// reads all modules in parent configurations
// `requires public` all automatic modules in parent configurations
p = parent;
while (p != null) {
for (ResolvedModule m : p.modules()) {
reads.add(m);
if (m.reference().descriptor().isAutomatic())
requiresPublic.add(m);
}
p = p.parent().orElse(null);
// `requires transitive` all automatic modules in parent
// configurations
for (Configuration parent : parents) {
parent.configurations()
.map(Configuration::modules)
.flatMap(Set::stream)
.forEach(m -> {
reads.add(m);
if (m.reference().descriptor().isAutomatic())
requiresTransitive.add(m);
});
}
}
g1.put(m1, reads);
g2.put(m1, requiresPublic);
g2.put(m1, requiresTransitive);
}
// Iteratively update g1 until there are no more requires public to propagate
// Iteratively update g1 until there are no more requires transitive
// to propagate
boolean changed;
Set<ResolvedModule> toAdd = new HashSet<>();
List<ResolvedModule> toAdd = new ArrayList<>();
do {
changed = false;
for (Set<ResolvedModule> m1Reads : g1.values()) {
for (ResolvedModule m2 : m1Reads) {
Set<ResolvedModule> m2RequiresPublic = g2.get(m2);
if (m2RequiresPublic != null) {
for (ResolvedModule m3 : m2RequiresPublic) {
Set<ResolvedModule> m2RequiresTransitive = g2.get(m2);
if (m2RequiresTransitive != null) {
for (ResolvedModule m3 : m2RequiresTransitive) {
if (!m1Reads.contains(m3)) {
// m1 reads m2, m2 requires public m3
// m1 reads m2, m2 requires transitive m3
// => need to add m1 reads m3
toAdd.add(m3);
}
@ -655,7 +695,7 @@ final class Resolver {
// source is exported to descriptor2
String source = export.source();
ModuleDescriptor other
= packageToExporter.put(source, descriptor2);
= packageToExporter.putIfAbsent(source, descriptor2);
if (other != null && other != descriptor2) {
// package might be local to descriptor1
@ -692,12 +732,8 @@ final class Resolver {
}
// provides S
for (Map.Entry<String, ModuleDescriptor.Provides> entry :
descriptor1.provides().entrySet()) {
String service = entry.getKey();
ModuleDescriptor.Provides provides = entry.getValue();
String pn = packageName(service);
for (ModuleDescriptor.Provides provides : descriptor1.provides()) {
String pn = packageName(provides.service());
if (!packageToExporter.containsKey(pn)) {
fail("Module %s does not read a module that exports %s",
descriptor1.name(), pn);
@ -717,6 +753,18 @@ final class Resolver {
}
/**
* Find a module of the given name in the parent configurations
*/
private ResolvedModule findInParent(String mn) {
for (Configuration parent : parents) {
Optional<ResolvedModule> om = parent.findModule(mn);
if (om.isPresent())
return om.get();
}
return null;
}
/**
* Invokes the beforeFinder to find method to find the given module.
@ -755,15 +803,18 @@ final class Resolver {
if (afterModules.isEmpty())
return beforeModules;
if (beforeModules.isEmpty() && parent == Configuration.empty())
if (beforeModules.isEmpty()
&& parents.size() == 1
&& parents.get(0) == Configuration.empty())
return afterModules;
Set<ModuleReference> result = new HashSet<>(beforeModules);
for (ModuleReference mref : afterModules) {
String name = mref.descriptor().name();
if (!beforeFinder.find(name).isPresent()
&& !parent.findModule(name).isPresent())
&& findInParent(name) == null) {
result.add(mref);
}
}
return result;

@ -39,6 +39,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@ -53,6 +54,7 @@ import jdk.internal.jimage.ImageReader;
import jdk.internal.jimage.ImageReaderFactory;
import jdk.internal.misc.JavaNetUriAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.ModuleBootstrap;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleHashes.HashSupplier;
import jdk.internal.module.SystemModules;
@ -64,7 +66,7 @@ import jdk.internal.perf.PerfCounter;
* run-time image.
*
* The modules linked into the run-time image are assumed to have the
* ConcealedPackages attribute.
* Packages attribute.
*/
class SystemModuleFinder implements ModuleFinder {
@ -102,8 +104,11 @@ class SystemModuleFinder implements ModuleFinder {
int n = names.length;
moduleCount.add(n);
Set<ModuleReference> mods = new HashSet<>(n);
Map<String, ModuleReference> map = new HashMap<>(n);
ModuleReference[] mods = new ModuleReference[n];
@SuppressWarnings(value = {"rawtypes", "unchecked"})
Entry<String, ModuleReference>[] map
= (Entry<String, ModuleReference>[])new Entry[n];
for (int i = 0; i < n; i++) {
ModuleDescriptor md = descriptors[i];
@ -111,16 +116,16 @@ class SystemModuleFinder implements ModuleFinder {
// create the ModuleReference
ModuleReference mref = toModuleReference(md, hashSupplier(i, names[i]));
mods.add(mref);
map.put(names[i], mref);
mods[i] = mref;
map[i] = Map.entry(names[i], mref);
// counters
packageCount.add(md.packages().size());
exportsCount.add(md.exports().size());
}
modules = Collections.unmodifiableSet(mods);
nameToModule = map;
modules = Set.of(mods);
nameToModule = Map.ofEntries(map);
initTime.addElapsedTimeFrom(t0);
}
@ -190,7 +195,7 @@ class SystemModuleFinder implements ModuleFinder {
new ModuleReference(md, uri, readerSupplier, hash);
// may need a reference to a patched module if --patch-module specified
mref = ModulePatcher.interposeIfNeeded(mref);
mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
return mref;
}
@ -199,7 +204,7 @@ class SystemModuleFinder implements ModuleFinder {
if (isFastPathSupported()) {
return new HashSupplier() {
@Override
public String generate(String algorithm) {
public byte[] generate(String algorithm) {
return SystemModules.MODULES_TO_HASH[index];
}
};
@ -213,7 +218,7 @@ class SystemModuleFinder implements ModuleFinder {
* It will get the recorded hashes from module-info.class.
*/
private static class Hashes {
static Map<String, String> hashes = new HashMap<>();
static Map<String, byte[]> hashes = new HashMap<>();
static void add(ModuleDescriptor descriptor) {
Optional<ModuleHashes> ohashes = descriptor.hashes();
@ -228,7 +233,7 @@ class SystemModuleFinder implements ModuleFinder {
return new HashSupplier() {
@Override
public String generate(String algorithm) {
public byte[] generate(String algorithm) {
return hashes.get(name);
}
};

@ -25,12 +25,12 @@
package java.lang.reflect;
import java.lang.annotation.Annotation;
import java.security.AccessController;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.reflect.ReflectionFactory;
import java.lang.annotation.Annotation;
/**
* The AccessibleObject class is the base class for Field, Method and
@ -81,8 +81,10 @@ public class AccessibleObject implements AnnotatedElement {
* <p>This method cannot be used to enable access to an object that is a
* {@link Member member} of a class in a different module to the caller and
* where the class is in a package that is not exported to the caller's
* module. Additionally, this method cannot be used to enable access to
* non-public members of {@code AccessibleObject} or {@link Module}.
* module. Additionally, if the member is non-public or its declaring
* class is non-public, then this method can only be used to enable access
* if the package is {@link Module#isOpen(String,Module) open} to at least
* the caller's module.
*
* <p>If there is a security manager, its
* {@code checkPermission} method is first called with a
@ -126,8 +128,10 @@ public class AccessibleObject implements AnnotatedElement {
* <p>This method cannot be used to enable access to an object that is a
* {@link Member member} of a class in a different module to the caller and
* where the class is in a package that is not exported to the caller's
* module. Additionally, this method cannot be used to enable access to
* non-public members of {@code AccessibleObject} or {@link Module}.
* module. Additionally, if the member is non-public or its declaring
* class is non-public, then this method can only be used to enable access
* if the package is {@link Module#isOpen(String,Module) open} to at least
* the caller's module.
*
* <p>If there is a security manager, its
* {@code checkPermission} method is first called with a
@ -138,6 +142,7 @@ public class AccessibleObject implements AnnotatedElement {
* @throws SecurityException if the request is denied
* @see SecurityManager#checkPermission
* @see ReflectPermission
* @see java.lang.invoke.MethodHandles#privateLookupIn
*/
public void setAccessible(boolean flag) {
AccessibleObject.checkPermission();
@ -161,35 +166,39 @@ public class AccessibleObject implements AnnotatedElement {
Module callerModule = caller.getModule();
Module declaringModule = declaringClass.getModule();
if (callerModule != declaringModule
&& callerModule != Object.class.getModule()) {
if (callerModule == declaringModule) return;
if (callerModule == Object.class.getModule()) return;
if (!declaringModule.isNamed()) return;
// check exports to target module
String pn = packageName(declaringClass);
if (!declaringModule.isExported(pn, callerModule)) {
String msg = "Unable to make member of "
+ declaringClass + " accessible: "
+ declaringModule + " does not export "
+ pn + " to " + callerModule;
Reflection.throwInaccessibleObjectException(msg);
}
// package is open to caller
String pn = packageName(declaringClass);
if (declaringModule.isOpen(pn, callerModule))
return;
// package is exported to caller and class/member is public
boolean isExported = declaringModule.isExported(pn, callerModule);
boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
int modifiers;
if (this instanceof Executable) {
modifiers = ((Executable) this).getModifiers();
} else {
modifiers = ((Field) this).getModifiers();
}
boolean isMemberPublic = Modifier.isPublic(modifiers);
if (isExported && isClassPublic && isMemberPublic)
return;
if (declaringClass == Module.class
|| declaringClass == AccessibleObject.class) {
int modifiers;
if (this instanceof Executable) {
modifiers = ((Executable) this).getModifiers();
} else {
modifiers = ((Field) this).getModifiers();
}
if (!Modifier.isPublic(modifiers)) {
String msg = "Cannot make a non-public member of "
+ declaringClass + " accessible";
Reflection.throwInaccessibleObjectException(msg);
}
}
// not accessible
String msg = "Unable to make ";
if (this instanceof Field)
msg += "field ";
msg += this + " accessible: " + declaringModule + " does not \"";
if (isClassPublic && isMemberPublic)
msg += "exports";
else
msg += "opens";
msg += " " + pn + "\" to " + callerModule;
Reflection.throwInaccessibleObjectException(msg);
}
/**

@ -27,23 +27,29 @@ package java.lang.reflect;
import java.lang.module.Configuration;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ResolvedModule;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.loader.ClassLoaderValue;
import jdk.internal.loader.Loader;
import jdk.internal.loader.LoaderPool;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.Modules;
import jdk.internal.module.ServicesCatalog;
import jdk.internal.module.ServicesCatalog.ServiceProvider;
import sun.security.util.SecurityConstants;
@ -55,7 +61,7 @@ import sun.security.util.SecurityConstants;
* Creating a layer informs the Java virtual machine about the classes that
* may be loaded from modules so that the Java virtual machine knows which
* module that each class is a member of. Each layer, except the {@link
* #empty() empty} layer, has a {@link #parent() parent}. </p>
* #empty() empty} layer, has at least one {@link #parents() parent}. </p>
*
* <p> Creating a layer creates a {@link Module} object for each {@link
* ResolvedModule} in the configuration. For each resolved module that is
@ -71,7 +77,11 @@ import sun.security.util.SecurityConstants;
* mapped to a single class loader or where each module is mapped to its own
* class loader. The {@link #defineModules defineModules} method is for more
* advanced cases where modules are mapped to custom class loaders by means of
* a function specified to the method. </p>
* a function specified to the method. Each of these methods has an instance
* and static variant. The instance methods create a layer with the receiver
* as the parent layer. The static methods are for more advanced cases where
* there can be more than one parent layer or a {@link Layer.Controller
* Controller} is needed to control modules in the layer. </p>
*
* <p> A Java virtual machine has at least one non-empty layer, the {@link
* #boot() boot} layer, that is created when the Java virtual machine is
@ -80,7 +90,7 @@ import sun.security.util.SecurityConstants;
* The modules in the boot layer are mapped to the bootstrap class loader and
* other class loaders that are <a href="../ClassLoader.html#builtinLoaders">
* built-in</a> into the Java virtual machine. The boot layer will often be
* the {@link #parent() parent} when creating additional layers. </p>
* the {@link #parents() parent} when creating additional layers. </p>
*
* <p> As when creating a {@code Configuration},
* {@link ModuleDescriptor#isAutomatic() automatic} modules receive
@ -123,30 +133,29 @@ public final class Layer {
// the empty Layer
private static final Layer EMPTY_LAYER
= new Layer(Configuration.empty(), null, null);
= new Layer(Configuration.empty(), List.of(), null);
// the configuration from which this Layer was created
private final Configuration cf;
// parent layer, null in the case of the empty layer
private final Layer parent;
// parent layers, empty in the case of the empty layer
private final List<Layer> parents;
// maps module name to jlr.Module
private final Map<String, Module> nameToModule;
/**
* Creates a new Layer from the modules in the given configuration.
*/
private Layer(Configuration cf,
Layer parent,
List<Layer> parents,
Function<String, ClassLoader> clf)
{
this.cf = cf;
this.parent = parent;
this.parents = parents; // no need to do defensive copy
Map<String, Module> map;
if (parent == null) {
if (parents.isEmpty()) {
map = Collections.emptyMap();
} else {
map = Module.defineModules(cf, clf, this);
@ -154,12 +163,230 @@ public final class Layer {
this.nameToModule = map; // no need to do defensive copy
}
/**
* Controls a layer. The static methods defined by {@link Layer} to create
* module layers return a {@code Controller} that can be used to control
* modules in the layer.
*
* @apiNote Care should be taken with {@code Controller} objects, they
* should never be shared with untrusted code.
*
* @since 9
*/
public static final class Controller {
private final Layer layer;
Controller(Layer layer) {
this.layer = layer;
}
/**
* Returns the layer that this object controls.
*
* @return the layer
*/
public Layer layer() {
return layer;
}
private void ensureInLayer(Module source) {
if (!layer.modules().contains(source))
throw new IllegalArgumentException(source + " not in layer");
}
/**
* Updates module {@code source} in the layer to read module
* {@code target}. This method is a no-op if {@code source} already
* reads {@code target}.
*
* @implNote <em>Read edges</em> added by this method are <em>weak</em>
* and do not prevent {@code target} from being GC'ed when {@code source}
* is strongly reachable.
*
* @param source
* The source module
* @param target
* The target module to read
*
* @return This controller
*
* @throws IllegalArgumentException
* If {@code source} is not in the layer
*
* @see Module#addReads
*/
public Controller addReads(Module source, Module target) {
Objects.requireNonNull(source);
Objects.requireNonNull(target);
ensureInLayer(source);
Modules.addReads(source, target);
return this;
}
/**
* Updates module {@code source} in the layer to open a package to
* module {@code target}. This method is a no-op if {@code source}
* already opens the package to at least {@code target}.
*
* @param source
* The source module
* @param pn
* The package name
* @param target
* The target module to read
*
* @return This controller
*
* @throws IllegalArgumentException
* If {@code source} is not in the layer or the package is not
* in the source module
*
* @see Module#addOpens
*/
public Controller addOpens(Module source, String pn, Module target) {
Objects.requireNonNull(source);
Objects.requireNonNull(source);
Objects.requireNonNull(target);
ensureInLayer(source);
Modules.addOpens(source, pn, target);
return this;
}
}
/**
* Creates a new layer, with this layer as its parent, by defining the
* modules in the given {@code Configuration} to the Java virtual machine.
* This method creates one class loader and defines all modules to that
* class loader.
* class loader. The {@link ClassLoader#getParent() parent} of each class
* loader is the given parent class loader. This method works exactly as
* specified by the static {@link
* #defineModulesWithOneLoader(Configuration,List,ClassLoader)
* defineModulesWithOneLoader} method when invoked with this layer as the
* parent. In other words, if this layer is {@code thisLayer} then this
* method is equivalent to invoking:
* <pre> {@code
* Layer.defineModulesWithOneLoader(cf, List.of(thisLayer), parentLoader).layer();
* }</pre>
*
* @param cf
* The configuration for the layer
* @param parentLoader
* The parent class loader for the class loader created by this
* method; may be {@code null} for the bootstrap class loader
*
* @return The newly created layer
*
* @throws IllegalArgumentException
* If the parent of the given configuration is not the configuration
* for this layer
* @throws LayerInstantiationException
* If all modules cannot be defined to the same class loader for any
* of the reasons listed above or the layer cannot be created because
* the configuration contains a module named "{@code java.base}" or
* a module with a package name starting with "{@code java.}"
* @throws SecurityException
* If {@code RuntimePermission("createClassLoader")} or
* {@code RuntimePermission("getClassLoader")} is denied by
* the security manager
*
* @see #findLoader
*/
public Layer defineModulesWithOneLoader(Configuration cf,
ClassLoader parentLoader) {
return defineModulesWithOneLoader(cf, List.of(this), parentLoader).layer();
}
/**
* Creates a new layer, with this layer as its parent, by defining the
* modules in the given {@code Configuration} to the Java virtual machine.
* Each module is defined to its own {@link ClassLoader} created by this
* method. The {@link ClassLoader#getParent() parent} of each class loader
* is the given parent class loader. This method works exactly as specified
* by the static {@link
* #defineModulesWithManyLoaders(Configuration,List,ClassLoader)
* defineModulesWithManyLoaders} method when invoked with this layer as the
* parent. In other words, if this layer is {@code thisLayer} then this
* method is equivalent to invoking:
* <pre> {@code
* Layer.defineModulesWithManyLoaders(cf, List.of(thisLayer), parentLoader).layer();
* }</pre>
*
* @param cf
* The configuration for the layer
* @param parentLoader
* The parent class loader for each of the class loaders created by
* this method; may be {@code null} for the bootstrap class loader
*
* @return The newly created layer
*
* @throws IllegalArgumentException
* If the parent of the given configuration is not the configuration
* for this layer
* @throws LayerInstantiationException
* If the layer cannot be created because the configuration contains
* a module named "{@code java.base}" or a module with a package
* name starting with "{@code java.}"
* @throws SecurityException
* If {@code RuntimePermission("createClassLoader")} or
* {@code RuntimePermission("getClassLoader")} is denied by
* the security manager
*
* @see #findLoader
*/
public Layer defineModulesWithManyLoaders(Configuration cf,
ClassLoader parentLoader) {
return defineModulesWithManyLoaders(cf, List.of(this), parentLoader).layer();
}
/**
* Creates a new layer, with this layer as its parent, by defining the
* modules in the given {@code Configuration} to the Java virtual machine.
* Each module is mapped, by name, to its class loader by means of the
* given function. This method works exactly as specified by the static
* {@link #defineModules(Configuration,List,Function) defineModules}
* method when invoked with this layer as the parent. In other words, if
* this layer is {@code thisLayer} then this method is equivalent to
* invoking:
* <pre> {@code
* Layer.defineModules(cf, List.of(thisLayer), clf).layer();
* }</pre>
*
* @param cf
* The configuration for the layer
* @param clf
* The function to map a module name to a class loader
*
* @return The newly created layer
*
* @throws IllegalArgumentException
* If the parent of the given configuration is not the configuration
* for this layer
* @throws LayerInstantiationException
* If creating the {@code Layer} fails for any of the reasons
* listed above, the layer cannot be created because the
* configuration contains a module named "{@code java.base}",
* a module with a package name starting with "{@code java.}" is
* mapped to a class loader other than the {@link
* ClassLoader#getPlatformClassLoader() platform class loader},
* or the function to map a module name to a class loader returns
* {@code null}
* @throws SecurityException
* If {@code RuntimePermission("getClassLoader")} is denied by
* the security manager
*/
public Layer defineModules(Configuration cf,
Function<String, ClassLoader> clf) {
return defineModules(cf, List.of(this), clf).layer();
}
/**
* Creates a new layer by defining the modules in the given {@code
* Configuration} to the Java virtual machine. This method creates one
* class loader and defines all modules to that class loader.
*
* <p> The class loader created by this method implements <em>direct
* delegation</em> when loading types from modules. When its {@link
@ -180,7 +407,7 @@ public final class Layer {
* <ul>
*
* <li><p> <em>Overlapping packages</em>: Two or more modules in the
* configuration have the same package (exported or concealed). </p></li>
* configuration have the same package. </p></li>
*
* <li><p> <em>Split delegation</em>: The resulting class loader would
* need to delegate to more than one class loader in order to load types
@ -194,19 +421,22 @@ public final class Layer {
*
* @param cf
* The configuration for the layer
* @param parentLayers
* The list parent layers in search order
* @param parentLoader
* The parent class loader for the class loader created by this
* method; may be {@code null} for the bootstrap class loader
*
* @return The newly created layer
* @return A controller that controls the newly created layer
*
* @throws IllegalArgumentException
* If the parent of the given configuration is not the configuration
* for this layer
* If the parent configurations do not match the configuration of
* the parent layers, including order
* @throws LayerInstantiationException
* If all modules cannot be defined to the same class loader for any
* of the reasons listed above or the layer cannot be created because
* the configuration contains a module named "{@code java.base}"
* the configuration contains a module named "{@code java.base}" or
* a module with a package name starting with "{@code java.}"
* @throws SecurityException
* If {@code RuntimePermission("createClassLoader")} or
* {@code RuntimePermission("getClassLoader")} is denied by
@ -214,29 +444,32 @@ public final class Layer {
*
* @see #findLoader
*/
public Layer defineModulesWithOneLoader(Configuration cf,
ClassLoader parentLoader)
public static Controller defineModulesWithOneLoader(Configuration cf,
List<Layer> parentLayers,
ClassLoader parentLoader)
{
checkConfiguration(cf);
List<Layer> parents = new ArrayList<>(parentLayers);
checkConfiguration(cf, parents);
checkCreateClassLoaderPermission();
checkGetClassLoaderPermission();
try {
Loader loader = new Loader(cf.modules(), parentLoader);
loader.initRemotePackageMap(cf, this);
return new Layer(cf, this, mn -> loader);
loader.initRemotePackageMap(cf, parents);
Layer layer = new Layer(cf, parents, mn -> loader);
return new Controller(layer);
} catch (IllegalArgumentException e) {
throw new LayerInstantiationException(e.getMessage());
}
}
/**
* Creates a new layer, with this layer as its parent, by defining the
* modules in the given {@code Configuration} to the Java virtual machine.
* Each module is defined to its own {@link ClassLoader} created by this
* method. The {@link ClassLoader#getParent() parent} of each class loader
* is the given parent class loader.
* Creates a new layer by defining the modules in the given {@code
* Configuration} to the Java virtual machine. Each module is defined to
* its own {@link ClassLoader} created by this method. The {@link
* ClassLoader#getParent() parent} of each class loader is the given parent
* class loader.
*
* <p> The class loaders created by this method implement <em>direct
* delegation</em> when loading types from modules. When {@link
@ -258,18 +491,21 @@ public final class Layer {
*
* @param cf
* The configuration for the layer
* @param parentLayers
* The list parent layers in search order
* @param parentLoader
* The parent class loader for each of the class loaders created by
* this method; may be {@code null} for the bootstrap class loader
*
* @return The newly created layer
* @return A controller that controls the newly created layer
*
* @throws IllegalArgumentException
* If the parent of the given configuration is not the configuration
* for this layer
* If the parent configurations do not match the configuration of
* the parent layers, including order
* @throws LayerInstantiationException
* If the layer cannot be created because the configuration contains
* a module named "{@code java.base}"
* a module named "{@code java.base}" or a module with a package
* name starting with "{@code java.}"
* @throws SecurityException
* If {@code RuntimePermission("createClassLoader")} or
* {@code RuntimePermission("getClassLoader")} is denied by
@ -277,37 +513,43 @@ public final class Layer {
*
* @see #findLoader
*/
public Layer defineModulesWithManyLoaders(Configuration cf,
ClassLoader parentLoader)
public static Controller defineModulesWithManyLoaders(Configuration cf,
List<Layer> parentLayers,
ClassLoader parentLoader)
{
checkConfiguration(cf);
List<Layer> parents = new ArrayList<>(parentLayers);
checkConfiguration(cf, parents);
checkCreateClassLoaderPermission();
checkGetClassLoaderPermission();
LoaderPool pool = new LoaderPool(cf, this, parentLoader);
LoaderPool pool = new LoaderPool(cf, parents, parentLoader);
try {
return new Layer(cf, this, pool::loaderFor);
Layer layer = new Layer(cf, parents, pool::loaderFor);
return new Controller(layer);
} catch (IllegalArgumentException e) {
throw new LayerInstantiationException(e.getMessage());
}
}
/**
* Creates a new layer, with this layer as its parent, by defining the
* modules in the given {@code Configuration} to the Java virtual machine.
* Creates a new layer by defining the modules in the given {@code
* Configuration} to the Java virtual machine.
* Each module is mapped, by name, to its class loader by means of the
* given function. The class loader delegation implemented by these class
* loaders must respect module readability. In addition, the caller needs
* to arrange that the class loaders are ready to load from these module
* before there are any attempts to load classes or resources.
* loaders must respect module readability. The class loaders should be
* {@link ClassLoader#registerAsParallelCapable parallel-capable} so as to
* avoid deadlocks during class loading. In addition, the entity creating
* a new layer with this method should arrange that the class loaders are
* ready to load from these module before there are any attempts to load
* classes or resources.
*
* <p> Creating a {@code Layer} can fail for the following reasons: </p>
*
* <ul>
*
* <li><p> Two or more modules with the same package (exported or
* concealed) are mapped to the same class loader. </p></li>
* <li><p> Two or more modules with the same package are mapped to the
* same class loader. </p></li>
*
* <li><p> A module is mapped to a class loader that already has a
* module of the same name defined to it. </p></li>
@ -328,26 +570,35 @@ public final class Layer {
*
* @param cf
* The configuration for the layer
* @param parentLayers
* The list parent layers in search order
* @param clf
* The function to map a module name to a class loader
*
* @return The newly created layer
* @return A controller that controls the newly created layer
*
* @throws IllegalArgumentException
* If the parent of the given configuration is not the configuration
* for this layer
* If the parent configurations do not match the configuration of
* the parent layers, including order
* @throws LayerInstantiationException
* If creating the {@code Layer} fails for any of the reasons
* listed above or the layer cannot be created because the
* configuration contains a module named "{@code java.base}"
* listed above, the layer cannot be created because the
* configuration contains a module named "{@code java.base}",
* a module with a package name starting with "{@code java.}" is
* mapped to a class loader other than the {@link
* ClassLoader#getPlatformClassLoader() platform class loader},
* or the function to map a module name to a class loader returns
* {@code null}
* @throws SecurityException
* If {@code RuntimePermission("getClassLoader")} is denied by
* the security manager
*/
public Layer defineModules(Configuration cf,
Function<String, ClassLoader> clf)
public static Controller defineModules(Configuration cf,
List<Layer> parentLayers,
Function<String, ClassLoader> clf)
{
checkConfiguration(cf);
List<Layer> parents = new ArrayList<>(parentLayers);
checkConfiguration(cf, parents);
Objects.requireNonNull(clf);
checkGetClassLoaderPermission();
@ -362,7 +613,8 @@ public final class Layer {
}
try {
return new Layer(cf, this, clf);
Layer layer = new Layer(cf, parents, clf);
return new Controller(layer);
} catch (IllegalArgumentException iae) {
// IAE is thrown by VM when defining the module fails
throw new LayerInstantiationException(iae.getMessage());
@ -370,13 +622,26 @@ public final class Layer {
}
private void checkConfiguration(Configuration cf) {
/**
* Checks that the parent configurations match the configuration of
* the parent layers.
*/
private static void checkConfiguration(Configuration cf,
List<Layer> parentLayers)
{
Objects.requireNonNull(cf);
Optional<Configuration> oparent = cf.parent();
if (!oparent.isPresent() || oparent.get() != this.configuration()) {
throw new IllegalArgumentException(
"Parent of configuration != configuration of this Layer");
List<Configuration> parentConfigurations = cf.parents();
if (parentLayers.size() != parentConfigurations.size())
throw new IllegalArgumentException("wrong number of parents");
int index = 0;
for (Layer parent : parentLayers) {
if (parent.configuration() != parentConfigurations.get(index)) {
throw new IllegalArgumentException(
"Parent of configuration != configuration of this Layer");
}
index++;
}
}
@ -463,18 +728,57 @@ public final class Layer {
/**
* Returns this layer's parent unless this is the {@linkplain #empty empty
* layer}, which has no parent.
* Returns the list of this layer's parents unless this is the
* {@linkplain #empty empty layer}, which has no parents and so an
* empty list is returned.
*
* @return This layer's parent
* @return The list of this layer's parents
*/
public Optional<Layer> parent() {
return Optional.ofNullable(parent);
public List<Layer> parents() {
return parents;
}
/**
* Returns a set of the modules in this layer.
* Returns an ordered stream of layers. The first element is is this layer,
* the remaining elements are the parent layers in DFS order.
*
* @implNote For now, the assumption is that the number of elements will
* be very low and so this method does not use a specialized spliterator.
*/
Stream<Layer> layers() {
List<Layer> allLayers = this.allLayers;
if (allLayers != null)
return allLayers.stream();
allLayers = new ArrayList<>();
Set<Layer> visited = new HashSet<>();
Deque<Layer> stack = new ArrayDeque<>();
visited.add(this);
stack.push(this);
while (!stack.isEmpty()) {
Layer layer = stack.pop();
allLayers.add(layer);
// push in reverse order
for (int i = layer.parents.size() - 1; i >= 0; i--) {
Layer parent = layer.parents.get(i);
if (!visited.contains(parent)) {
visited.add(parent);
stack.push(parent);
}
}
}
this.allLayers = allLayers = Collections.unmodifiableList(allLayers);
return allLayers.stream();
}
private volatile List<Layer> allLayers;
/**
* Returns the set of the modules in this layer.
*
* @return A possibly-empty unmodifiable set of the modules in this layer
*/
@ -486,7 +790,11 @@ public final class Layer {
/**
* Returns the module with the given name in this layer, or if not in this
* layer, the {@linkplain #parent parent} layer.
* layer, the {@linkplain #parents parents} layers. Finding a module in
* parent layers is equivalent to invoking {@code findModule} on each
* parent, in search order, until the module is found or all parents have
* been searched. In a <em>tree of layers</em> then this is equivalent to
* a depth-first search.
*
* @param name
* The name of the module to find
@ -496,17 +804,25 @@ public final class Layer {
* parent layer
*/
public Optional<Module> findModule(String name) {
Module m = nameToModule.get(Objects.requireNonNull(name));
Objects.requireNonNull(name);
Module m = nameToModule.get(name);
if (m != null)
return Optional.of(m);
return parent().flatMap(l -> l.findModule(name));
return layers()
.skip(1) // skip this layer
.map(l -> l.nameToModule)
.filter(map -> map.containsKey(name))
.map(map -> map.get(name))
.findAny();
}
/**
* Returns the {@code ClassLoader} for the module with the given name. If
* a module of the given name is not in this layer then the {@link #parent}
* layer is checked.
* a module of the given name is not in this layer then the {@link #parents
* parent} layers are searched in the manner specified by {@link
* #findModule(String) findModule}.
*
* <p> If there is a security manager then its {@code checkPermission}
* method is called with a {@code RuntimePermission("getClassLoader")}
@ -527,20 +843,32 @@ public final class Layer {
* @throws SecurityException if denied by the security manager
*/
public ClassLoader findLoader(String name) {
Module m = nameToModule.get(Objects.requireNonNull(name));
if (m != null)
return m.getClassLoader();
Optional<Layer> ol = parent();
if (ol.isPresent())
return ol.get().findLoader(name);
throw new IllegalArgumentException("Module " + name
+ " not known to this layer");
Optional<Module> om = findModule(name);
// can't use map(Module::getClassLoader) as class loader can be null
if (om.isPresent()) {
return om.get().getClassLoader();
} else {
throw new IllegalArgumentException("Module " + name
+ " not known to this layer");
}
}
/**
* Returns a string describing this layer.
*
* @return A possibly empty string describing this layer
*/
@Override
public String toString() {
return modules().stream()
.map(Module::getName)
.collect(Collectors.joining(", "));
}
/**
* Returns the <em>empty</em> layer. There are no modules in the empty
* layer. It has no parent.
* layer. It has no parents.
*
* @return The empty layer
*/
@ -572,39 +900,12 @@ public final class Layer {
if (servicesCatalog != null)
return servicesCatalog;
Map<String, Set<ServiceProvider>> map = new HashMap<>();
for (Module m : nameToModule.values()) {
ModuleDescriptor descriptor = m.getDescriptor();
for (Provides provides : descriptor.provides().values()) {
String service = provides.service();
Set<ServiceProvider> providers
= map.computeIfAbsent(service, k -> new HashSet<>());
for (String pn : provides.providers()) {
providers.add(new ServiceProvider(m, pn));
}
}
}
ServicesCatalog catalog = new ServicesCatalog() {
@Override
public void register(Module module) {
throw new UnsupportedOperationException();
}
@Override
public Set<ServiceProvider> findServices(String service) {
Set<ServiceProvider> providers = map.get(service);
if (providers == null) {
return Collections.emptySet();
} else {
return Collections.unmodifiableSet(providers);
}
}
};
synchronized (this) {
servicesCatalog = this.servicesCatalog;
if (servicesCatalog == null) {
this.servicesCatalog = servicesCatalog = catalog;
servicesCatalog = ServicesCatalog.create();
nameToModule.values().forEach(servicesCatalog::register);
this.servicesCatalog = servicesCatalog;
}
}
@ -612,4 +913,36 @@ public final class Layer {
}
private volatile ServicesCatalog servicesCatalog;
/**
* Record that this layer has at least one module defined to the given
* class loader.
*/
void bindToLoader(ClassLoader loader) {
// CLV.computeIfAbsent(loader, (cl, clv) -> new CopyOnWriteArrayList<>())
List<Layer> list = CLV.get(loader);
if (list == null) {
list = new CopyOnWriteArrayList<>();
List<Layer> previous = CLV.putIfAbsent(loader, list);
if (previous != null) list = previous;
}
list.add(this);
}
/**
* Returns a stream of the layers that have at least one module defined to
* the given class loader.
*/
static Stream<Layer> layers(ClassLoader loader) {
List<Layer> list = CLV.get(loader);
if (list != null) {
return list.stream();
} else {
return Stream.empty();
}
}
// the list of layers with modules defined to a class loader
private static final ClassLoaderValue<List<Layer>> CLV = new ClassLoaderValue<>();
}

File diff suppressed because it is too large Load Diff

@ -42,6 +42,7 @@ package java.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
@ -62,13 +63,14 @@ import java.util.jar.JarEntry;
import java.util.spi.ResourceBundleControlProvider;
import java.util.spi.ResourceBundleProvider;
import jdk.internal.loader.BootLoader;
import jdk.internal.misc.JavaUtilResourceBundleAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import sun.security.action.GetPropertyAction;
import sun.util.locale.BaseLocale;
import sun.util.locale.LocaleObjectCache;
import sun.util.locale.provider.ResourceBundleProviderSupport;
import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;
@ -247,7 +249,8 @@ import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;
* used to load resource bundles. If no service provider is available, or if
* none of the service providers returns a resource bundle and the caller module
* doesn't have its own service provider, the {@code getBundle} factory method
* searches for resource bundles local to the caller module. The resource bundle
* searches for resource bundles that are local in the caller module and that
* are visible to the class loader of the caller module. The resource bundle
* formats for local module searching are "java.class" and "java.properties".
*
* <h3>ResourceBundle.Control</h3>
@ -372,6 +375,18 @@ public abstract class ResourceBundle {
public void setName(ResourceBundle bundle, String name) {
bundle.name = name;
}
@Override
public ResourceBundle getBundle(String baseName, Locale locale, Module module) {
// use the given module as the caller to bypass the access check
return getBundleImpl(module, module, getLoader(module),
baseName, locale, Control.INSTANCE);
}
@Override
public ResourceBundle newResourceBundle(Class<? extends ResourceBundle> bundleClass) {
return ResourceBundleProviderHelper.newResourceBundle(bundleClass);
}
});
}
@ -999,6 +1014,14 @@ public abstract class ResourceBundle {
* <code>getBundle(baseName, Locale.getDefault(), module)</code>
* </blockquote>
*
* <p> Resource bundles in named modules may be encapsulated. When
* the resource bundle is loaded from a provider, the caller module
* must have an appropriate <i>uses</i> clause in its <i>module descriptor</i>
* to declare that the module uses implementations of {@code "baseName"Provider}.
* When the resource bundle is loaded from the specified module, it is
* subject to the encapsulation rules specified by
* {@link Module#getResourceAsStream Module.getResourceAsStream}.
*
* @param baseName the base name of the resource bundle,
* a fully qualified class name
* @param module the module for which the resource bundle is searched
@ -1024,10 +1047,19 @@ public abstract class ResourceBundle {
* Gets a resource bundle using the specified base name and locale
* on behalf of the specified module.
*
* <p> Resource bundles in named modules may be encapsulated. When
* the resource bundle is loaded from a provider, the caller module
* must have an appropriate <i>uses</i> clause in its <i>module descriptor</i>
* to declare that the module uses implementations of {@code "baseName"Provider}.
* When the resource bundle is loaded from the specified module, it is
* subject to the encapsulation rules specified by
* {@link Module#getResourceAsStream Module.getResourceAsStream}.
*
* <p>
* If the given {@code module} is a named module, this method will
* load the service providers for {@link java.util.spi.ResourceBundleProvider}
* and also resource bundles local in the given module (refer to the
* and also resource bundles that are local in the given module or that
* are visible to the class loader of the given module (refer to the
* <a href="#bundleprovider">Resource Bundles in Named Modules</a> section
* for details).
*
@ -1035,9 +1067,8 @@ public abstract class ResourceBundle {
* If the given {@code module} is an unnamed module, then this method is
* equivalent to calling {@link #getBundle(String, Locale, ClassLoader)
* getBundle(baseName, targetLocale, module.getClassLoader()} to load
* resource bundles that are in unnamed modules visible to the
* class loader of the given unnamed module. It will not find resource
* bundles from named modules.
* resource bundles that are visible to the class loader of the given
* unnamed module.
*
* @param baseName the base name of the resource bundle,
* a fully qualified class name
@ -1126,7 +1157,8 @@ public abstract class ResourceBundle {
* Resource bundles in a named module are private to that module. If
* the caller is in a named module, this method will find resource bundles
* from the service providers of {@link java.util.spi.ResourceBundleProvider}
* and also find resource bundles private to the caller's module.
* and also find resource bundles that are in the caller's module or
* that are visible to the given class loader.
* If the caller is in a named module and the given {@code loader} is
* different than the caller's class loader, or if the caller is not in
* a named module, this method will not find resource bundles from named
@ -1587,13 +1619,20 @@ public abstract class ResourceBundle {
// get resource bundles for a named module only
// if loader is the module's class loader
if (loader == ml || (ml == null && loader == RBClassLoader.INSTANCE)) {
return getBundleImpl(baseName, locale, loader, module, control);
return getBundleImpl(module, module, loader, baseName, locale, control);
}
}
// find resource bundles from unnamed module
Module module = loader != null ? loader.getUnnamedModule()
: ClassLoader.getSystemClassLoader().getUnnamedModule();
return getBundleImpl(baseName, locale, loader, module, control);
Module unnamedModule = loader != null
? loader.getUnnamedModule()
: ClassLoader.getSystemClassLoader().getUnnamedModule();
if (caller == null) {
throw new InternalError("null caller");
}
Module callerModule = caller.getModule();
return getBundleImpl(callerModule, unnamedModule, loader, baseName, locale, control);
}
private static ResourceBundle getBundleFromModule(Class<?> caller,
@ -1602,19 +1641,21 @@ public abstract class ResourceBundle {
Locale locale,
Control control) {
Objects.requireNonNull(module);
if (caller.getModule() != module) {
Module callerModule = caller.getModule();
if (callerModule != module) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(GET_CLASSLOADER_PERMISSION);
}
}
return getBundleImpl(baseName, locale, getLoader(module), module, control);
return getBundleImpl(callerModule, module, getLoader(module), baseName, locale, control);
}
private static ResourceBundle getBundleImpl(String baseName,
Locale locale,
ClassLoader loader,
private static ResourceBundle getBundleImpl(Module callerModule,
Module module,
ClassLoader loader,
String baseName,
Locale locale,
Control control) {
if (locale == null || control == null) {
throw new NullPointerException();
@ -1661,7 +1702,8 @@ public abstract class ResourceBundle {
throw new IllegalArgumentException("Invalid Control: getCandidateLocales");
}
bundle = findBundle(cacheKey, module, candidateLocales, formats, 0, control, baseBundle);
bundle = findBundle(callerModule, module, cacheKey,
candidateLocales, formats, 0, control, baseBundle);
// If the loaded bundle is the base bundle and exactly for the
// requested locale or the only candidate locale, then take the
@ -1710,8 +1752,9 @@ public abstract class ResourceBundle {
return valid;
}
private static ResourceBundle findBundle(CacheKey cacheKey,
private static ResourceBundle findBundle(Module callerModule,
Module module,
CacheKey cacheKey,
List<Locale> candidateLocales,
List<String> formats,
int index,
@ -1720,7 +1763,8 @@ public abstract class ResourceBundle {
Locale targetLocale = candidateLocales.get(index);
ResourceBundle parent = null;
if (index != candidateLocales.size() - 1) {
parent = findBundle(cacheKey, module, candidateLocales, formats, index + 1,
parent = findBundle(callerModule, module, cacheKey,
candidateLocales, formats, index + 1,
control, baseBundle);
} else if (baseBundle != null && Locale.ROOT.equals(targetLocale)) {
return baseBundle;
@ -1764,10 +1808,10 @@ public abstract class ResourceBundle {
if (bundle != NONEXISTENT_BUNDLE) {
CacheKey constKey = (CacheKey) cacheKey.clone();
trace("findBundle: %d %s %s formats: %s%n", index, candidateLocales, cacheKey, formats);
try {
if (module.isNamed()) {
bundle = loadBundle(cacheKey, formats, control, module);
bundle = loadBundle(cacheKey, formats, control, module, callerModule);
} else {
bundle = loadBundle(cacheKey, formats, control, expiredBundle);
}
@ -1794,39 +1838,60 @@ public abstract class ResourceBundle {
private static final String UNKNOWN_FORMAT = "";
/*
* Loads a ResourceBundle in named modules
*/
private static ResourceBundle loadBundle(CacheKey cacheKey,
List<String> formats,
Control control,
Module module) {
Module module,
Module callerModule) {
String baseName = cacheKey.getName();
Locale targetLocale = cacheKey.getLocale();
ResourceBundle bundle = null;
if (cacheKey.hasProviders()) {
bundle = loadBundleFromProviders(baseName, targetLocale,
cacheKey.getProviders(), cacheKey);
if (callerModule == module) {
bundle = loadBundleFromProviders(baseName,
targetLocale,
cacheKey.getProviders(),
cacheKey);
} else {
// load from provider if the caller module has access to the
// service type and also declares `uses`
ClassLoader loader = getLoader(module);
Class<ResourceBundleProvider> svc =
getResourceBundleProviderType(baseName, loader);
if (svc != null
&& Reflection.verifyModuleAccess(callerModule, svc)
&& callerModule.canUse(svc)) {
bundle = loadBundleFromProviders(baseName,
targetLocale,
cacheKey.getProviders(),
cacheKey);
}
}
if (bundle != null) {
cacheKey.setFormat(UNKNOWN_FORMAT);
}
}
// If none of providers returned a bundle and the caller has no provider,
// look up module-local bundles.
// look up module-local bundles or from the class path
if (bundle == null && !cacheKey.callerHasProvider()) {
String bundleName = control.toBundleName(baseName, targetLocale);
for (String format : formats) {
try {
switch (format) {
case "java.class":
PrivilegedAction<ResourceBundle> pa = ()
-> ResourceBundleProviderSupport
.loadResourceBundle(module, bundleName);
bundle = AccessController.doPrivileged(pa, null, GET_CLASSLOADER_PERMISSION);
bundle = ResourceBundleProviderHelper
.loadResourceBundle(callerModule, module, baseName, targetLocale);
break;
case "java.properties":
bundle = ResourceBundleProviderSupport.loadPropertyResourceBundle(module, bundleName);
bundle = ResourceBundleProviderHelper
.loadPropertyResourceBundle(callerModule, module, baseName, targetLocale);
break;
default:
throw new InternalError("unexpected format: " + format);
@ -1844,29 +1909,46 @@ public abstract class ResourceBundle {
return bundle;
}
/**
* Returns a ServiceLoader that will find providers that are bound to
* a given named module.
*/
private static ServiceLoader<ResourceBundleProvider> getServiceLoader(Module module,
String baseName) {
String baseName)
{
if (!module.isNamed()) {
return null;
}
PrivilegedAction<ClassLoader> pa = module::getClassLoader;
ClassLoader loader = AccessController.doPrivileged(pa);
return getServiceLoader(module, loader, baseName);
ClassLoader loader = getLoader(module);
Class<ResourceBundleProvider> service =
getResourceBundleProviderType(baseName, loader);
if (service != null && Reflection.verifyModuleAccess(module, service)) {
try {
// locate providers that are visible to the class loader
// ServiceConfigurationError will be thrown if the module
// does not declare `uses` the service type
return ServiceLoader.load(service, loader, module);
} catch (ServiceConfigurationError e) {
// "uses" not declared
return null;
}
}
return null;
}
/**
* Returns a ServiceLoader that will find providers that are bound to
* a given module that may be named or unnamed.
*/
private static ServiceLoader<ResourceBundleProvider> getServiceLoader(Module module,
ClassLoader loader,
String baseName)
/**
* Returns the service type of the given baseName that is visible
* to the given class loader
*/
private static Class<ResourceBundleProvider>
getResourceBundleProviderType(String baseName, ClassLoader loader)
{
// Look up <baseName> + "Provider"
String providerName = baseName + "Provider";
// Use the class loader of the getBundle caller so that the caller's
// visibility of the provider type is checked.
Class<ResourceBundleProvider> service = AccessController.doPrivileged(
return AccessController.doPrivileged(
new PrivilegedAction<>() {
@Override
public Class<ResourceBundleProvider> run() {
@ -1881,16 +1963,6 @@ public abstract class ResourceBundle {
return null;
}
});
if (service != null && Reflection.verifyModuleAccess(module, service)) {
try {
return ServiceLoader.load(service, loader, module);
} catch (ServiceConfigurationError e) {
// "uses" not declared: load bundle local in the module
return null;
}
}
return null;
}
/**
@ -1914,6 +1986,7 @@ public abstract class ResourceBundle {
cacheKey.callerHasProvider = Boolean.TRUE;
}
ResourceBundle bundle = provider.getBundle(baseName, locale);
trace("provider %s %s locale: %s bundle: %s%n", provider, baseName, locale, bundle);
if (bundle != null) {
return bundle;
}
@ -3016,6 +3089,14 @@ public abstract class ResourceBundle {
* indicates that this method is being called because the previously
* loaded resource bundle has expired.
*
* @implSpec
*
* Resource bundles in named modules are subject to the encapsulation
* rules specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
* A resource bundle in a named module visible to the given class loader
* is accessible when the package of the resource file corresponding
* to the resource bundle is open unconditionally.
*
* <p>The default implementation instantiates a
* <code>ResourceBundle</code> as follows.
*
@ -3026,12 +3107,15 @@ public abstract class ResourceBundle {
* locale)}.</li>
*
* <li>If <code>format</code> is <code>"java.class"</code>, the
* {@link Class} specified by the bundle name is loaded by calling
* {@link ClassLoader#loadClass(String)}. Then, a
* <code>ResourceBundle</code> is instantiated by calling {@link
* Class#newInstance()}. Note that the <code>reload</code> flag is
* ignored for loading class-based resource bundles in this default
* implementation.</li>
* {@link Class} specified by the bundle name is loaded with the
* given class loader. If the {@code Class} is found and accessible
* then the <code>ResourceBundle</code> is instantiated. The
* resource bundle is accessible if the package of the bundle class file
* is open unconditionally; otherwise, {@code IllegalAccessException}
* will be thrown.
* Note that the <code>reload</code> flag is ignored for loading
* class-based resource bundles in this default implementation.
* </li>
*
* <li>If <code>format</code> is <code>"java.properties"</code>,
* {@link #toResourceName(String, String) toResourceName(bundlename,
@ -3105,7 +3189,6 @@ public abstract class ResourceBundle {
/*
* Legacy mechanism to locate resource bundle in unnamed module only
* that is visible to the given loader and accessible to the given caller.
* This only finds resources on the class path but not in named modules.
*/
String bundleName = toBundleName(baseName, locale);
ResourceBundle bundle = null;
@ -3117,18 +3200,15 @@ public abstract class ResourceBundle {
if (ResourceBundle.class.isAssignableFrom(c)) {
@SuppressWarnings("unchecked")
Class<ResourceBundle> bundleClass = (Class<ResourceBundle>)c;
Module m = bundleClass.getModule();
// This doesn't allow unnamed modules to find bundles in
// named modules other than via the service loader mechanism.
// Otherwise, this will make the newBundle method to be
// caller-sensitive in order to verify access check.
// So migrating resource bundles to named module can't
// just export the package (in general, legacy resource
// bundles have split package if they are packaged separate
// from the consumer.)
if (bundleClass.getModule().isNamed()) {
throw new IllegalAccessException("unnamed modules can't load " + bundleName
+ " in named module " + bundleClass.getModule().getName());
// To access a resource bundle in a named module,
// either class-based or properties-based, the resource
// bundle must be opened unconditionally,
// same rule as accessing a resource file.
if (m.isNamed() && !m.isOpen(bundleClass.getPackageName())) {
throw new IllegalAccessException("unnamed module can't load " +
bundleClass.getName() + " in " + m.toString());
}
try {
// bundle in a unnamed module
@ -3502,4 +3582,173 @@ public abstract class ResourceBundle {
return null;
}
}
private static class ResourceBundleProviderHelper {
/**
* Returns a new ResourceBundle instance of the given bundleClass
*/
static ResourceBundle newResourceBundle(Class<? extends ResourceBundle> bundleClass) {
try {
@SuppressWarnings("unchecked")
Constructor<? extends ResourceBundle> ctor =
bundleClass.getConstructor();
if (!Modifier.isPublic(ctor.getModifiers())) {
return null;
}
// java.base may not be able to read the bundleClass's module.
PrivilegedAction<Void> pa = () -> { ctor.setAccessible(true); return null;};
AccessController.doPrivileged(pa);
try {
return ctor.newInstance((Object[]) null);
} catch (InvocationTargetException e) {
uncheckedThrow(e);
} catch (InstantiationException | IllegalAccessException e) {
throw new InternalError(e);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e);
}
return null;
}
/**
* Loads a {@code ResourceBundle} of the given {@code bundleName} local to
* the given {@code module}. If not found, search the bundle class
* that is visible from the module's class loader.
*
* The caller module is used for access check only.
*/
static ResourceBundle loadResourceBundle(Module callerModule,
Module module,
String baseName,
Locale locale)
{
String bundleName = Control.INSTANCE.toBundleName(baseName, locale);
try {
PrivilegedAction<Class<?>> pa = () -> Class.forName(module, bundleName);
Class<?> c = AccessController.doPrivileged(pa, null, GET_CLASSLOADER_PERMISSION);
trace("local in %s %s caller %s: %s%n", module, bundleName, callerModule, c);
if (c == null) {
// if not found from the given module, locate resource bundle
// that is visible to the module's class loader
ClassLoader loader = getLoader(module);
if (loader != null) {
c = Class.forName(bundleName, false, loader);
} else {
c = BootLoader.loadClassOrNull(bundleName);
}
trace("loader for %s %s caller %s: %s%n", module, bundleName, callerModule, c);
}
if (c != null && ResourceBundle.class.isAssignableFrom(c)) {
@SuppressWarnings("unchecked")
Class<ResourceBundle> bundleClass = (Class<ResourceBundle>) c;
Module m = bundleClass.getModule();
if (!isAccessible(callerModule, m, bundleClass.getPackageName())) {
trace(" %s does not have access to %s/%s%n", callerModule,
m.getName(), bundleClass.getPackageName());
return null;
}
return newResourceBundle(bundleClass);
}
} catch (ClassNotFoundException e) {}
return null;
}
/**
* Tests if resources of the given package name from the given module are
* open to the caller module.
*/
static boolean isAccessible(Module callerModule, Module module, String pn) {
if (!module.isNamed() || callerModule == module)
return true;
return module.isOpen(pn, callerModule);
}
/**
* Loads properties of the given {@code bundleName} local in the given
* {@code module}. If the .properties is not found or not open
* to the caller module to access, it will find the resource that
* is visible to the module's class loader.
*
* The caller module is used for access check only.
*/
static ResourceBundle loadPropertyResourceBundle(Module callerModule,
Module module,
String baseName,
Locale locale)
throws IOException
{
String bundleName = Control.INSTANCE.toBundleName(baseName, locale);
PrivilegedAction<InputStream> pa = () -> {
try {
String resourceName = Control.INSTANCE
.toResourceName0(bundleName, "properties");
if (resourceName == null) {
return null;
}
trace("local in %s %s caller %s%n", module, resourceName, callerModule);
// if the package is in the given module but not opened
// locate it from the given module first.
String pn = toPackageName(bundleName);
trace(" %s/%s is accessible to %s : %s%n",
module.getName(), pn, callerModule,
isAccessible(callerModule, module, pn));
if (isAccessible(callerModule, module, pn)) {
InputStream in = module.getResourceAsStream(resourceName);
if (in != null) {
return in;
}
}
ClassLoader loader = module.getClassLoader();
trace("loader for %s %s caller %s%n", module, resourceName, callerModule);
try {
if (loader != null) {
return loader.getResourceAsStream(resourceName);
} else {
URL url = BootLoader.findResource(resourceName);
if (url != null) {
return url.openStream();
}
}
} catch (Exception e) {}
return null;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
try (InputStream stream = AccessController.doPrivileged(pa)) {
if (stream != null) {
return new PropertyResourceBundle(stream);
} else {
return null;
}
} catch (UncheckedIOException e) {
throw e.getCause();
}
}
private static String toPackageName(String bundleName) {
int i = bundleName.lastIndexOf('.');
return i != -1 ? bundleName.substring(0, i) : "";
}
}
private static final boolean TRACE_ON = Boolean.valueOf(
GetPropertyAction.privilegedGetProperty("resource.bundle.debug", "false"));
private static void trace(String format, Object... params) {
if (TRACE_ON)
System.out.format(format, params);
}
}

File diff suppressed because it is too large Load Diff

@ -25,37 +25,47 @@
package java.util.spi;
import jdk.internal.misc.JavaUtilResourceBundleAccess;
import jdk.internal.misc.SharedSecrets;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.reflect.Module;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import sun.util.locale.provider.ResourceBundleProviderSupport;
import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;
/**
* {@code AbstractResourceBundleProvider} is an abstract class for helping
* implement the {@link ResourceBundleProvider} interface.
* {@code AbstractResourceBundleProvider} is an abstract class that provides
* the basic support for a provider implementation class for
* {@link ResourceBundleProvider}.
*
* <p>
* Resource bundles can be packaged in a named module separated from
* the <em>caller module</em> loading the resource bundle, i.e. the module
* calling {@link ResourceBundle#getBundle(String)}. For the caller module
* to load a resource bundle "{@code com.example.app.MyResources}"
* from another module and a service interface named
* "{@code com.example.app.MyResourcesProvider}",
* the <em>bundle provider module</em> can provide the implementation class
* Resource bundles can be packaged in one or more
* named modules, <em>bundle modules</em>. The <em>consumer</em> of the
* resource bundle is the one calling {@link ResourceBundle#getBundle(String)}.
* In order for the consumer module to load a resource bundle
* "{@code com.example.app.MyResources}" provided by another module,
* it will use the {@linkplain java.util.ServiceLoader service loader}
* mechanism. A service interface named "{@code com.example.app.MyResourcesProvider}"
* must be defined and a <em>bundle provider module</em> will provide an
* implementation class of "{@code com.example.app.MyResourcesProvider}"
* as follows:
*
* <pre><code>
* import com.example.app.MyResourcesProvider;
* class MyResourcesProviderImpl extends AbstractResourceBundleProvider
* implements MyResourcesProvider
* {</code>
* {@code @Override
* {
* protected String toBundleName(String baseName, Locale locale) {
* // return the bundle name per the naming of the resource bundle
* :
* }
*
* public ResourceBundle getBundle(String baseName, Locale locale) {
* // this module only provides bundles in french
* if (locale.equals(Locale.FRENCH)) {
@ -63,7 +73,7 @@ import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;
* }
* return null;
* }
* }}</pre>
* }</code></pre>
*
* @see <a href="../ResourceBundle.html#bundleprovider">
* Resource Bundles in Named Modules</a>
@ -73,6 +83,9 @@ import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;
* @since 9
*/
public abstract class AbstractResourceBundleProvider implements ResourceBundleProvider {
private static final JavaUtilResourceBundleAccess RB_ACCESS =
SharedSecrets.getJavaUtilResourceBundleAccess();
private static final String FORMAT_CLASS = "java.class";
private static final String FORMAT_PROPERTIES = "java.properties";
@ -112,7 +125,23 @@ public abstract class AbstractResourceBundleProvider implements ResourceBundlePr
/**
* Returns the bundle name for the given {@code baseName} and {@code
* locale}. This method is called from the default implementation of the
* locale} that this provider provides.
*
* @apiNote
* A resource bundle provider may package its resource bundles in the
* same package as the base name of the resource bundle if the package
* is not split among other named modules. If there are more than one
* bundle providers providing the resource bundle of a given base name,
* the resource bundles can be packaged with per-language grouping
* or per-region grouping to eliminate the split packages.
*
* <p>For example, if {@code baseName} is {@code "p.resources.Bundle"} then
* the resource bundle name of {@code "p.resources.Bundle"} of
* {@code Locale("ja",&nbsp;"",&nbsp;"XX")} and {@code Locale("en")}
* could be {@code "p.resources.ja.Bundle_ja_&thinsp;_XX"} and
* {@code p.resources.Bundle_en"} respectively
*
* <p> This method is called from the default implementation of the
* {@link #getBundle(String, Locale)} method.
*
* @implNote The default implementation of this method is the same as the
@ -126,27 +155,28 @@ public abstract class AbstractResourceBundleProvider implements ResourceBundlePr
*/
protected String toBundleName(String baseName, Locale locale) {
return ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT)
.toBundleName(baseName, locale);
.toBundleName(baseName, locale);
}
/**
* Returns a {@code ResourceBundle} for the given {@code baseName} and
* {@code locale}. This method calls the
* {@link #toBundleName(String, Locale) toBundleName} method to get the
* bundle name for the {@code baseName} and {@code locale}. The formats
* specified by the constructor will be searched to find the resource
* bundle.
* {@code locale}.
*
* @implNote
* The default implementation of this method will find the resource bundle
* local to the module of this provider.
* The default implementation of this method calls the
* {@link #toBundleName(String, Locale) toBundleName} method to get the
* bundle name for the {@code baseName} and {@code locale} and finds the
* resource bundle of the bundle name local in the module of this provider.
* It will only search the formats specified when this provider was
* constructed.
*
* @param baseName the base bundle name of the resource bundle, a fully
* qualified class name.
* @param locale the locale for which the resource bundle should be instantiated
* @return {@code ResourceBundle} of the given {@code baseName} and {@code locale},
* or null if no resource bundle is found
* @throws NullPointerException if {@code baseName} or {@code locale} is null
* @return {@code ResourceBundle} of the given {@code baseName} and
* {@code locale}, or {@code null} if no resource bundle is found
* @throws NullPointerException if {@code baseName} or {@code locale} is
* {@code null}
* @throws UncheckedIOException if any IO exception occurred during resource
* bundle loading
*/
@ -159,13 +189,9 @@ public abstract class AbstractResourceBundleProvider implements ResourceBundlePr
for (String format : formats) {
try {
if (FORMAT_CLASS.equals(format)) {
PrivilegedAction<ResourceBundle> pa = () ->
ResourceBundleProviderSupport
.loadResourceBundle(module, bundleName);
bundle = AccessController.doPrivileged(pa, null, GET_CLASSLOADER_PERMISSION);
bundle = loadResourceBundle(module, bundleName);
} else if (FORMAT_PROPERTIES.equals(format)) {
bundle = ResourceBundleProviderSupport
.loadPropertyResourceBundle(module, bundleName);
bundle = loadPropertyResourceBundle(module, bundleName);
}
if (bundle != null) {
break;
@ -176,4 +202,61 @@ public abstract class AbstractResourceBundleProvider implements ResourceBundlePr
}
return bundle;
}
/*
* Returns the ResourceBundle of .class format if found in the module
* of this provider.
*/
private static ResourceBundle loadResourceBundle(Module module, String bundleName)
{
PrivilegedAction<Class<?>> pa = () -> Class.forName(module, bundleName);
Class<?> c = AccessController.doPrivileged(pa, null, GET_CLASSLOADER_PERMISSION);
if (c != null && ResourceBundle.class.isAssignableFrom(c)) {
@SuppressWarnings("unchecked")
Class<ResourceBundle> bundleClass = (Class<ResourceBundle>) c;
return RB_ACCESS.newResourceBundle(bundleClass);
}
return null;
}
/*
* Returns the ResourceBundle of .property format if found in the module
* of this provider.
*/
private static ResourceBundle loadPropertyResourceBundle(Module module,
String bundleName)
throws IOException
{
String resourceName = toResourceName(bundleName, "properties");
if (resourceName == null) {
return null;
}
PrivilegedAction<InputStream> pa = () -> {
try {
return module.getResourceAsStream(resourceName);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
try (InputStream stream = AccessController.doPrivileged(pa)) {
if (stream != null) {
return new PropertyResourceBundle(stream);
} else {
return null;
}
} catch (UncheckedIOException e) {
throw e.getCause();
}
}
private static String toResourceName(String bundleName, String suffix) {
if (bundleName.contains("://")) {
return null;
}
StringBuilder sb = new StringBuilder(bundleName.length() + 1 + suffix.length());
sb.append(bundleName.replace('.', '/')).append('.').append(suffix);
return sb.toString();
}
}

@ -71,8 +71,8 @@ public class BootLoader {
private static final ServicesCatalog SERVICES_CATALOG = ServicesCatalog.create();
// ClassLoaderValue map for boot class loader
private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP =
new ConcurrentHashMap<>();
private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP
= new ConcurrentHashMap<>();
/**
* Returns the unnamed module for the boot loader.
@ -111,14 +111,27 @@ public class BootLoader {
}
/**
* Returns a URL to a resource in a named module defined to the boot loader.
* Loads the Class object with the given name in the given module
* defined to the boot loader. Returns {@code null} if not found.
*/
public static Class<?> loadClass(Module module, String name) {
Class<?> c = loadClassOrNull(name);
if (c != null && c.getModule() == module) {
return c;
} else {
return null;
}
}
/**
* Returns a URL to a resource in a module defined to the boot loader.
*/
public static URL findResource(String mn, String name) throws IOException {
return ClassLoaders.bootLoader().findResource(mn, name);
}
/**
* Returns an input stream to a resource in a named module defined to the
* Returns an input stream to a resource in a module defined to the
* boot loader.
*/
public static InputStream findResourceAsStream(String mn, String name)
@ -128,9 +141,8 @@ public class BootLoader {
}
/**
* Returns the URL to the given resource if the resource can be located
* on the boot class path. This method does not locate a resource in any
* of the named modules defined to the boot loader.
* Returns the URL to the given resource in any of the modules
* defined to the boot loader and the boot class path.
*/
public static URL findResource(String name) {
return ClassLoaders.bootLoader().findResource(name);
@ -138,8 +150,7 @@ public class BootLoader {
/**
* Returns an Iterator to iterate over the resources of the given name
* on the boot class path. This method does not locate resources in any
* of the named modules defined to the boot loader.
* in any of the modules defined to the boot loader.
*/
public static Enumeration<URL> findResources(String name) throws IOException {
return ClassLoaders.bootLoader().findResources(name);

@ -29,8 +29,10 @@ import java.io.File;
import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleReference;
import java.lang.module.ModuleReader;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
@ -91,7 +93,7 @@ public class BuiltinClassLoader
{
static {
if (!ClassLoader.registerAsParallelCapable())
throw new InternalError();
throw new InternalError("Unable to register as parallel capable");
}
// parent ClassLoader
@ -117,7 +119,7 @@ public class BuiltinClassLoader
if (mref.location().isPresent()) {
try {
url = mref.location().get().toURL();
} catch (MalformedURLException e) { }
} catch (MalformedURLException | IllegalArgumentException e) { }
}
this.loader = loader;
this.mref = mref;
@ -141,6 +143,9 @@ public class BuiltinClassLoader
// maps a module reference to a module reader
private final Map<ModuleReference, ModuleReader> moduleToReader;
// cache of resource name -> list of URLs.
// used only for resources that are not in module packages
private volatile SoftReference<Map<String, List<URL>>> resourceCache;
/**
* Create a new instance.
@ -161,6 +166,8 @@ public class BuiltinClassLoader
* the types in the module visible.
*/
public void loadModule(ModuleReference mref) {
assert !VM.isModuleSystemInited();
String mn = mref.descriptor().name();
if (nameToModule.putIfAbsent(mn, mref) != null) {
throw new InternalError(mn + " already defined to this loader");
@ -195,32 +202,20 @@ public class BuiltinClassLoader
*/
@Override
public URL findResource(String mn, String name) throws IOException {
ModuleReference mref = nameToModule.get(mn);
if (mref == null)
return null; // not defined to this class loader
URL url = null;
URL url;
try {
url = AccessController.doPrivileged(
new PrivilegedExceptionAction<URL>() {
@Override
public URL run() throws IOException {
URI u = moduleReaderFor(mref).find(name).orElse(null);
if (u != null) {
try {
return u.toURL();
} catch (MalformedURLException e) { }
}
return null;
}
});
} catch (PrivilegedActionException pae) {
throw (IOException) pae.getCause();
if (mn != null) {
// find in module
ModuleReference mref = nameToModule.get(mn);
if (mref != null) {
url = findResource(mref, name);
}
} else {
// find on class path
url = findResourceOnClassPath(name);
}
// check access to the URL
return checkURL(url);
return checkURL(url); // check access before returning
}
/**
@ -232,69 +227,233 @@ public class BuiltinClassLoader
{
// Need URL to resource when running with a security manager so that
// the right permission check is done.
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
if (System.getSecurityManager() != null || mn == null) {
URL url = findResource(mn, name);
return (url != null) ? url.openStream() : null;
} else {
ModuleReference mref = nameToModule.get(mn);
if (mref == null)
return null; // not defined to this class loader
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<InputStream>() {
@Override
public InputStream run() throws IOException {
return moduleReaderFor(mref).open(name).orElse(null);
}
});
} catch (PrivilegedActionException pae) {
throw (IOException) pae.getCause();
}
}
}
/**
* Finds the resource with the given name on the class path of this class
* loader.
*/
@Override
public URL findResource(String name) {
if (ucp != null) {
PrivilegedAction<URL> pa = () -> ucp.findResource(name, false);
URL url = AccessController.doPrivileged(pa);
return checkURL(url);
// find in module defined to this loader, no security manager
ModuleReference mref = nameToModule.get(mn);
if (mref != null) {
return moduleReaderFor(mref).open(name).orElse(null);
} else {
return null;
}
}
/**
* Finds a resource with the given name in the modules defined to this
* class loader or its class path.
*/
@Override
public URL findResource(String name) {
String pn = ResourceHelper.getPackageName(name);
LoadedModule module = packageToModule.get(pn);
if (module != null) {
// resource is in a package of a module defined to this loader
if (module.loader() == this
&& (name.endsWith(".class") || isOpen(module.mref(), pn))) {
try {
return findResource(module.name(), name); // checks URL
} catch (IOException ioe) {
return null;
}
}
} else {
// not in a module package but may be in module defined to this loader
try {
List<URL> urls = findMiscResource(name);
if (!urls.isEmpty()) {
URL url = urls.get(0);
if (url != null) {
return checkURL(url); // check access before returning
}
}
} catch (IOException ioe) {
return null;
}
}
// search class path
URL url = findResourceOnClassPath(name);
return checkURL(url);
}
/**
* Returns an enumeration of URL objects to all the resources with the
* given name on the class path of this class loader.
* given name in modules defined to this class loader or on the class
* path of this loader.
*/
@Override
public Enumeration<URL> findResources(String name) throws IOException {
if (ucp != null) {
List<URL> result = new ArrayList<>();
PrivilegedAction<Enumeration<URL>> pa = () -> ucp.findResources(name, false);
Enumeration<URL> e = AccessController.doPrivileged(pa);
while (e.hasMoreElements()) {
URL url = checkURL(e.nextElement());
List<URL> checked = new ArrayList<>(); // list of checked URLs
String pn = ResourceHelper.getPackageName(name);
LoadedModule module = packageToModule.get(pn);
if (module != null) {
// resource is in a package of a module defined to this loader
if (module.loader() == this
&& (name.endsWith(".class") || isOpen(module.mref(), pn))) {
URL url = findResource(module.name(), name); // checks URL
if (url != null) {
result.add(url);
checked.add(url);
}
}
return Collections.enumeration(result); // checked URLs
} else {
return Collections.emptyEnumeration();
// not in a package of a module defined to this loader
for (URL url : findMiscResource(name)) {
url = checkURL(url);
if (url != null) {
checked.add(url);
}
}
}
// search class path
Enumeration<URL> e = findResourcesOnClassPath(name);
while (e.hasMoreElements()) {
URL url = checkURL(e.nextElement());
if (url != null) {
checked.add(url);
}
}
return Collections.enumeration(checked);
}
/**
* Returns the list of URLs to a "miscellaneous" resource in modules
* defined to this loader. A miscellaneous resource is not in a module
* package, e.g. META-INF/services/p.S.
*
* The cache used by this method avoids repeated searching of all modules.
*/
private List<URL> findMiscResource(String name) throws IOException {
SoftReference<Map<String, List<URL>>> ref = this.resourceCache;
Map<String, List<URL>> map = (ref != null) ? ref.get() : null;
if (map != null) {
List<URL> urls = map.get(name);
if (urls != null)
return urls;
}
// search all modules for the resource
List<URL> urls;
try {
urls = AccessController.doPrivileged(
new PrivilegedExceptionAction<>() {
@Override
public List<URL> run() throws IOException {
List<URL> result = new ArrayList<>();
for (ModuleReference mref : nameToModule.values()) {
URI u = moduleReaderFor(mref).find(name).orElse(null);
if (u != null) {
try {
result.add(u.toURL());
} catch (MalformedURLException |
IllegalArgumentException e) {
}
}
}
return result;
}
});
} catch (PrivilegedActionException pae) {
throw (IOException) pae.getCause();
}
// only cache resources after all modules have been defined
if (VM.isModuleSystemInited()) {
if (map == null) {
map = new ConcurrentHashMap<>();
this.resourceCache = new SoftReference<>(map);
}
if (urls.isEmpty())
urls = Collections.emptyList();
map.putIfAbsent(name, urls);
}
return urls;
}
/**
* Returns the URL to a resource in a module or {@code null} if not found.
*/
private URL findResource(ModuleReference mref, String name) throws IOException {
URI u;
if (System.getSecurityManager() == null) {
u = moduleReaderFor(mref).find(name).orElse(null);
} else {
try {
u = AccessController.doPrivileged(new PrivilegedExceptionAction<> () {
@Override
public URI run() throws IOException {
return moduleReaderFor(mref).find(name).orElse(null);
}
});
} catch (PrivilegedActionException pae) {
throw (IOException) pae.getCause();
}
}
if (u != null) {
try {
return u.toURL();
} catch (MalformedURLException | IllegalArgumentException e) { }
}
return null;
}
/**
* Returns the URL to a resource in a module. Returns {@code null} if not found
* or an I/O error occurs.
*/
private URL findResourceOrNull(ModuleReference mref, String name) {
try {
return findResource(mref, name);
} catch (IOException ignore) {
return null;
}
}
/**
* Returns a URL to a resource on the class path.
*/
private URL findResourceOnClassPath(String name) {
if (ucp != null) {
if (System.getSecurityManager() == null) {
return ucp.findResource(name, false);
} else {
PrivilegedAction<URL> pa = () -> ucp.findResource(name, false);
return AccessController.doPrivileged(pa);
}
} else {
// no class path
return null;
}
}
/**
* Returns the URLs of all resources of the given name on the class path.
*/
private Enumeration<URL> findResourcesOnClassPath(String name) {
if (ucp != null) {
if (System.getSecurityManager() == null) {
return ucp.findResources(name, false);
} else {
PrivilegedAction<Enumeration<URL>> pa;
pa = () -> ucp.findResources(name, false);
return AccessController.doPrivileged(pa);
}
} else {
// no class path
return Collections.emptyEnumeration();
}
}
// -- finding/loading classes
@ -335,24 +494,30 @@ public class BuiltinClassLoader
}
/**
* Finds the class with the specified binary name in a given module.
* This method returns {@code null} if the class cannot be found.
* Finds the class with the specified binary name in a module.
* This method returns {@code null} if the class cannot be found
* or not defined in the specified module.
*/
@Override
protected Class<?> findClass(String mn, String cn) {
ModuleReference mref = nameToModule.get(mn);
if (mref == null)
return null; // not defined to this class loader
if (mn != null) {
// find the candidate module for this class
LoadedModule loadedModule = findLoadedModule(mn, cn);
if (loadedModule == null) {
return null;
}
// find the candidate module for this class
LoadedModule loadedModule = findLoadedModule(cn);
if (loadedModule == null || !loadedModule.name().equals(mn)) {
return null; // module name does not match
// attempt to load class in module defined to this loader
assert loadedModule.loader() == this;
return findClassInModuleOrNull(loadedModule, cn);
}
// attempt to load class in module defined to this loader
assert loadedModule.loader() == this;
return findClassInModuleOrNull(loadedModule, cn);
// search class path
if (ucp != null) {
return findClassOnClassPathOrNull(cn);
}
return null;
}
/**
@ -440,6 +605,21 @@ public class BuiltinClassLoader
return packageToModule.get(pn);
}
/**
* Find the candidate loaded module for the given class name
* in the named module. Returns {@code null} if the named module
* is not defined to this class loader or does not contain
* the API package for the class.
*/
private LoadedModule findLoadedModule(String mn, String cn) {
LoadedModule loadedModule = findLoadedModule(cn);
if (loadedModule != null && mn.equals(loadedModule.name())) {
return loadedModule;
} else {
return null;
}
}
/**
* Finds the class with the specified binary name if in a module
* defined to this ClassLoader.
@ -447,8 +627,12 @@ public class BuiltinClassLoader
* @return the resulting Class or {@code null} if not found
*/
private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) {
PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule);
return AccessController.doPrivileged(pa);
if (System.getSecurityManager() == null) {
return defineClass(cn, loadedModule);
} else {
PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule);
return AccessController.doPrivileged(pa);
}
}
/**
@ -457,10 +641,21 @@ public class BuiltinClassLoader
* @return the resulting Class or {@code null} if not found
*/
private Class<?> findClassOnClassPathOrNull(String cn) {
return AccessController.doPrivileged(
new PrivilegedAction<Class<?>>() {
String path = cn.replace('.', '/').concat(".class");
if (System.getSecurityManager() == null) {
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(cn, res);
} catch (IOException ioe) {
// TBD on how I/O errors should be propagated
}
}
return null;
} else {
// avoid use of lambda here
PrivilegedAction<Class<?>> pa = new PrivilegedAction<>() {
public Class<?> run() {
String path = cn.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
@ -471,7 +666,9 @@ public class BuiltinClassLoader
}
return null;
}
});
};
return AccessController.doPrivileged(pa);
}
}
/**
@ -662,13 +859,13 @@ public class BuiltinClassLoader
sealBase = url;
}
return definePackage(pn,
specTitle,
specVersion,
specVendor,
implTitle,
implVersion,
implVendor,
sealBase);
specTitle,
specVersion,
specVendor,
implTitle,
implVersion,
implVendor,
sealBase);
}
/**
@ -759,6 +956,27 @@ public class BuiltinClassLoader
}
};
/**
* Returns true if the given module opens the given package
* unconditionally.
*
* @implNote This method currently iterates over each of the open
* packages. This will be replaced once the ModuleDescriptor.Opens
* API is updated.
*/
private boolean isOpen(ModuleReference mref, String pn) {
ModuleDescriptor descriptor = mref.descriptor();
if (descriptor.isOpen())
return true;
for (ModuleDescriptor.Opens opens : descriptor.opens()) {
String source = opens.source();
if (!opens.isQualified() && source.equals(pn)) {
return true;
}
}
return false;
}
/**
* Checks access to the given URL. We use URLClassPath for consistent
* checking with java.net.URLClassLoader.

@ -48,13 +48,19 @@ import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.SecureClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import jdk.internal.misc.SharedSecrets;
/**
* A class loader that loads classes and resources from a collection of
@ -119,7 +125,7 @@ public final class Loader extends SecureClassLoader {
if (mref.location().isPresent()) {
try {
url = mref.location().get().toURL();
} catch (MalformedURLException e) { }
} catch (MalformedURLException | IllegalArgumentException e) { }
}
this.mref = mref;
this.url = url;
@ -141,7 +147,7 @@ public final class Loader extends SecureClassLoader {
LoaderPool pool,
ClassLoader parent)
{
super(parent);
super("Loader-" + resolvedModule.name(), parent);
this.pool = pool;
this.parent = parent;
@ -200,12 +206,12 @@ public final class Loader extends SecureClassLoader {
* @param cf the Configuration containing at least modules to be defined to
* this class loader
*
* @param parentLayer the parent Layer
* @param parentLayers the parent Layers
*/
public Loader initRemotePackageMap(Configuration cf, Layer parentLayer) {
public Loader initRemotePackageMap(Configuration cf,
List<Layer> parentLayers)
{
for (String name : nameToModule.keySet()) {
ResolvedModule resolvedModule = cf.findModule(name).get();
assert resolvedModule.configuration() == cf;
@ -228,17 +234,15 @@ public final class Loader extends SecureClassLoader {
} else {
// find the layer contains the module that is read
Layer layer = parentLayer;
while (layer != null) {
if (layer.configuration() == other.configuration()) {
break;
}
layer = layer.parent().orElse(null);
}
assert layer != null;
// find the layer for the target module
Layer layer = parentLayers.stream()
.map(parent -> findLayer(parent, other.configuration()))
.flatMap(Optional::stream)
.findAny()
.orElseThrow(() ->
new InternalError("Unable to find parent layer"));
// find the class loader for the module in the layer
// find the class loader for the module
// For now we use the platform loader for modules defined to the
// boot loader
assert layer.findModule(mn).isPresent();
@ -268,7 +272,6 @@ public final class Loader extends SecureClassLoader {
throw new IllegalArgumentException("Package "
+ pn + " cannot be imported from multiple loaders");
}
}
}
}
@ -278,6 +281,17 @@ public final class Loader extends SecureClassLoader {
return this;
}
/**
* Find the layer corresponding to the given configuration in the tree
* of layers rooted at the given parent.
*/
private Optional<Layer> findLayer(Layer parent, Configuration cf) {
return SharedSecrets.getJavaLangReflectModuleAccess().layers(parent)
.filter(l -> l.configuration() == cf)
.findAny();
}
/**
* Returns the loader pool that this loader is in or {@code null} if this
* loader is not in a loader pool.
@ -296,12 +310,14 @@ public final class Loader extends SecureClassLoader {
*/
@Override
protected URL findResource(String mn, String name) throws IOException {
ModuleReference mref = nameToModule.get(mn);
ModuleReference mref = (mn != null) ? nameToModule.get(mn) : null;
if (mref == null)
return null; // not defined to this class loader
// locate resource
URL url = null;
try {
return AccessController.doPrivileged(
url = AccessController.doPrivileged(
new PrivilegedExceptionAction<URL>() {
@Override
public URL run() throws IOException {
@ -309,16 +325,89 @@ public final class Loader extends SecureClassLoader {
if (ouri.isPresent()) {
try {
return ouri.get().toURL();
} catch (MalformedURLException e) { }
} catch (MalformedURLException |
IllegalArgumentException e) { }
}
return null;
}
}, acc);
});
} catch (PrivilegedActionException pae) {
throw (IOException) pae.getCause();
} catch (SecurityException se) {
return null;
}
// check access with permissions restricted by ACC
if (url != null && System.getSecurityManager() != null) {
try {
URL urlToCheck = url;
url = AccessController.doPrivileged(
new PrivilegedExceptionAction<URL>() {
@Override
public URL run() throws IOException {
return URLClassPath.checkURL(urlToCheck);
}
}, acc);
} catch (PrivilegedActionException pae) {
url = null;
}
}
return url;
}
@Override
public URL findResource(String name) {
URL url = null;
String pn = ResourceHelper.getPackageName(name);
LoadedModule module = localPackageToModule.get(pn);
if (module != null) {
if (name.endsWith(".class") || isOpen(module.mref(), pn)) {
try {
url = findResource(module.name(), name);
} catch (IOException ioe) {
// ignore
}
}
} else {
for (ModuleReference mref : nameToModule.values()) {
try {
url = findResource(mref.descriptor().name(), name);
if (url != null)
break;
} catch (IOException ioe) {
// ignore
}
}
}
return url;
}
@Override
public Enumeration<URL> findResources(String name) throws IOException {
List<URL> urls = new ArrayList<>();
String pn = ResourceHelper.getPackageName(name);
LoadedModule module = localPackageToModule.get(pn);
if (module != null) {
if (name.endsWith(".class") || isOpen(module.mref(), pn)) {
try {
URL url = findResource(module.name(), name);
if (url != null)
urls.add(url);
} catch (IOException ioe) {
// ignore
}
}
} else {
for (ModuleReference mref : nameToModule.values()) {
try {
URL url = findResource(mref.descriptor().name(), name);
if (url != null)
urls.add(url);
} catch (IOException ioe) {
// ignore
}
}
}
return Collections.enumeration(urls);
}
@ -544,4 +633,24 @@ public final class Loader extends SecureClassLoader {
}
}
/**
* Returns true if the given module opens the given package
* unconditionally.
*
* @implNote This method currently iterates over each of the open
* packages. This will be replaced once the ModuleDescriptor.Opens
* API is updated.
*/
private boolean isOpen(ModuleReference mref, String pn) {
ModuleDescriptor descriptor = mref.descriptor();
if (descriptor.isOpen())
return true;
for (ModuleDescriptor.Opens opens : descriptor.opens()) {
String source = opens.source();
if (!opens.isQualified() && source.equals(pn)) {
return true;
}
}
return false;
}
}

@ -26,10 +26,10 @@
package jdk.internal.loader;
import java.lang.module.Configuration;
import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule;
import java.lang.reflect.Layer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
@ -51,7 +51,7 @@ public final class LoaderPool {
* created with the given parent class loader as its parent.
*/
public LoaderPool(Configuration cf,
Layer parentLayer,
List<Layer> parentLayers,
ClassLoader parentLoader)
{
Map<String, Loader> loaders = new HashMap<>();
@ -63,7 +63,7 @@ public final class LoaderPool {
this.loaders = loaders;
// complete the initialization
loaders.values().forEach(l -> l.initRemotePackageMap(cf, parentLayer));
loaders.values().forEach(l -> l.initRemotePackageMap(cf, parentLayers));
}

@ -0,0 +1,64 @@
/*
* Copyright (c) 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 jdk.internal.loader;
import jdk.internal.module.Checks;
/**
* Helper class for Class#getResource, Module#getResourceAsStream, and other
* methods that locate a resource in a module.
*/
public final class ResourceHelper {
private ResourceHelper() { }
/**
* Returns the <em>package name</em> for a resource.
*/
public static String getPackageName(String name) {
int index = name.lastIndexOf('/');
if (index != -1) {
return name.substring(0, index).replace("/", ".");
} else {
return "";
}
}
/**
* Returns true if the resource is a <em>simple resource</em> that can
* never be encapsulated. Resources ending in "{@code .class}" or where
* the package name is not a Java identifier are resources that can
* never be encapsulated.
*/
public static boolean isSimpleResource(String name) {
int len = name.length();
if (len > 6 && name.endsWith(".class")) {
return true;
}
if (!Checks.isJavaIdentifier(getPackageName(name))) {
return true;
}
return false;
}
}

@ -29,6 +29,7 @@ import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Executable;
import java.lang.reflect.Layer;
import java.lang.reflect.Method;
import java.lang.reflect.Module;
import java.net.URL;
import java.security.AccessControlContext;
@ -36,12 +37,19 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import jdk.internal.module.ServicesCatalog;
import jdk.internal.reflect.ConstantPool;
import sun.reflect.annotation.AnnotationType;
import sun.nio.ch.Interruptible;
public interface JavaLangAccess {
/**
* Returns a {@code Method} object that reflects the specified public
* member method of the given class. Returns {@code null} if the
* method is not defined.
*/
Method getMethodOrNull(Class<?> klass, String name, Class<?>... parameterTypes);
/** Return the constant pool for a class. */
ConstantPool getConstantPool(Class<?> klass);
@ -135,17 +143,6 @@ public interface JavaLangAccess {
*/
Layer getBootLayer();
/**
* Returns the ServicesCatalog for the given class loader.
*/
ServicesCatalog getServicesCatalog(ClassLoader cl);
/**
* Returns the ServicesCatalog for the given class loader, creating it
* if doesn't already exist.
*/
ServicesCatalog createOrGetServicesCatalog(ClassLoader cl);
/**
* Returns the ConcurrentHashMap used as a storage for ClassLoaderValue(s)
* associated with the given class loader, creating it if it doesn't already exist.

@ -27,24 +27,26 @@ package jdk.internal.misc;
import java.io.PrintStream;
import java.lang.module.Configuration;
import jdk.internal.module.ModuleHashes;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Version;
import java.lang.module.ModuleFinder;
import java.util.Collection;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.net.URI;
import java.nio.file.Path;
import java.util.Map;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import jdk.internal.module.ModuleHashes;
/**
* Provides access to non-public methods in java.lang.module.
*/
@ -52,28 +54,59 @@ import java.util.function.Supplier;
public interface JavaLangModuleAccess {
/**
* Returns {@code ModuleDescriptor.Requires} of the given modifier
* Creates a builder for building a module with the given module name.
*
* @param strict
* Indicates whether module names are checked or not
*/
ModuleDescriptor.Builder newModuleBuilder(String mn, boolean strict);
/**
* Creates a builder for building an open module with the given module name.
*
* @param strict
* Indicates whether module names are checked or not
*/
ModuleDescriptor.Builder newOpenModuleBuilder(String mn, boolean strict);
/**
* Returns a {@code ModuleDescriptor.Requires} of the given modifiers
* and module name.
*/
Requires newRequires(Set<Requires.Modifier> ms, String mn);
/**
* Returns an unqualified {@code ModuleDescriptor.Exports}
* of the given package name.
* of the given modifiers and package name source.
*/
Exports newExports(String source);
Exports newExports(Set<Exports.Modifier> ms,
String source);
/**
* Returns a qualified {@code ModuleDescriptor.Exports}
* of the given package name and targets.
* of the given modifiers, package name source and targets.
*/
Exports newExports(String source, Set<String> targets);
Exports newExports(Set<Exports.Modifier> ms,
String source,
Set<String> targets);
/**
* Returns an unqualified {@code ModuleDescriptor.Opens}
* of the given modifiers and package name source.
*/
Opens newOpens(Set<Opens.Modifier> ms, String source);
/**
* Returns a qualified {@code ModuleDescriptor.Opens}
* of the given modifiers, package name source and targets.
*/
Opens newOpens(Set<Opens.Modifier> ms, String source, Set<String> targets);
/**
* Returns a {@code ModuleDescriptor.Provides}
* of the given service name and providers.
*/
Provides newProvides(String service, Set<String> providers);
Provides newProvides(String service, List<String> providers);
/**
* Returns a {@code ModuleDescriptor.Version} of the given version.
@ -89,19 +122,22 @@ public interface JavaLangModuleAccess {
* Returns a new {@code ModuleDescriptor} instance.
*/
ModuleDescriptor newModuleDescriptor(String name,
boolean open,
boolean automatic,
boolean synthetic,
Set<Requires> requires,
Set<String> uses,
Set<Exports> exports,
Map<String, Provides> provides,
Set<Opens> opens,
Set<String> uses,
Set<Provides> provides,
Version version,
String mainClass,
String osName,
String osArch,
String osVersion,
Set<String> packages,
ModuleHashes hashes);
ModuleHashes hashes,
int hashCode);
/**
* Returns the object with the hashes of other modules

@ -29,6 +29,7 @@ import java.lang.module.ModuleDescriptor;
import java.lang.reflect.Layer;
import java.lang.reflect.Module;
import java.net.URI;
import java.util.stream.Stream;
import jdk.internal.module.ServicesCatalog;
@ -70,16 +71,37 @@ public interface JavaLangReflectModuleAccess {
*/
void addExports(Module m1, String pkg, Module m2);
/**
* Updates module m1 to open a package to module m2. Opening the
* package does not result in a strong reference to m2 (m2 can be GC'ed).
*/
void addOpens(Module m1, String pkg, Module m2);
/**
* Updates a module m to export a package to all modules.
*/
void addExportsToAll(Module m, String pkg);
/**
* Updates a module m to open a package to all modules.
*/
void addOpensToAll(Module m, String pkg);
/**
* Updates a module m to export a package to all unnamed modules.
*/
void addExportsToAllUnnamed(Module m, String pkg);
/**
* Updates a module m to open a package to all unnamed modules.
*/
void addOpensToAllUnnamed(Module m, String pkg);
/**
* Updates a module m to use a service.
*/
void addUses(Module m, Class<?> service);
/**
* Add a package to the given module.
*/
@ -90,4 +112,15 @@ public interface JavaLangReflectModuleAccess {
*/
ServicesCatalog getServicesCatalog(Layer layer);
/**
* Returns an ordered stream of layers. The first element is is the
* given layer, the remaining elements are its parents, in DFS order.
*/
Stream<Layer> layers(Layer layer);
/**
* Returns a stream of the layers that have modules defined to the
* given class loader.
*/
Stream<Layer> layers(ClassLoader loader);
}

@ -25,6 +25,7 @@
package jdk.internal.misc;
import java.lang.reflect.Module;
import java.util.Locale;
import java.util.ResourceBundle;
@ -51,4 +52,16 @@ public interface JavaUtilResourceBundleAccess {
* Sets the bundle's base name to the given name.
*/
void setName(ResourceBundle bundle, String name);
/**
* Returns a {@code ResourceBundle} of the given baseName and locale
* loaded on behalf of the given module with no caller module
* access check.
*/
ResourceBundle getBundle(String baseName, Locale locale, Module module);
/**
* Instantiates a {@code ResourceBundle} of the given bundle class.
*/
ResourceBundle newResourceBundle(Class<? extends ResourceBundle> bundleClass);
}

@ -26,6 +26,7 @@
package jdk.internal.misc;
import java.lang.module.ModuleDescriptor;
import java.util.ResourceBundle;
import java.util.jar.JarFile;
import java.io.Console;
import java.io.FileDescriptor;
@ -294,6 +295,8 @@ public class SharedSecrets {
}
public static JavaUtilResourceBundleAccess getJavaUtilResourceBundleAccess() {
if (javaUtilResourceBundleAccess == null)
unsafe.ensureClassInitialized(ResourceBundle.class);
return javaUtilResourceBundleAccess;
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2016, 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
@ -26,12 +26,14 @@ package jdk.internal.module;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Version;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -40,7 +42,7 @@ import jdk.internal.misc.SharedSecrets;
/**
* This builder is optimized for reconstituting ModuleDescriptor
* for installed modules. The validation should be done at jlink time.
* for system modules. The validation should be done at jlink time.
*
* 1. skip name validation
* 2. ignores dependency hashes.
@ -53,66 +55,137 @@ final class Builder {
private static final JavaLangModuleAccess jlma =
SharedSecrets.getJavaLangModuleAccess();
private static final Set<Requires.Modifier> MANDATED =
Collections.singleton(Requires.Modifier.MANDATED);
private static final Set<Requires.Modifier> PUBLIC =
Collections.singleton(Requires.Modifier.PUBLIC);
// Static cache of the most recently seen Version to cheaply deduplicate
// most Version objects. JDK modules have the same version.
static Version cachedVersion;
/**
* Returns a {@link Requires} for a dependence on a module
* with the given (and possibly empty) set of modifiers.
*/
public static Requires newRequires(Set<Requires.Modifier> mods,
String mn)
{
return jlma.newRequires(mods, mn);
}
/**
* Returns a {@link Exports} for a qualified export, with
* the given (and possibly empty) set of modifiers,
* to a set of target modules.
*/
public static Exports newExports(Set<Exports.Modifier> ms,
String pn,
Set<String> targets) {
return jlma.newExports(ms, pn, targets);
}
/**
* Returns an {@link Opens} for an unqualified open with a given set of
* modifiers.
*/
public static Opens newOpens(Set<Opens.Modifier> ms, String pn) {
return jlma.newOpens(ms, pn);
}
/**
* Returns an {@link Opens} for a qualified opens, with
* the given (and possibly empty) set of modifiers,
* to a set of target modules.
*/
public static Opens newOpens(Set<Opens.Modifier> ms,
String pn,
Set<String> targets) {
return jlma.newOpens(ms, pn, targets);
}
/**
* Returns a {@link Exports} for an unqualified export with a given set
* of modifiers.
*/
public static Exports newExports(Set<Exports.Modifier> ms, String pn) {
return jlma.newExports(ms, pn);
}
/**
* Returns a {@link Provides} for a service with a given list of
* implementation classes.
*/
public static Provides newProvides(String st, List<String> pcs) {
return jlma.newProvides(st, pcs);
}
final String name;
final Set<Requires> requires;
final Set<Exports> exports;
final Map<String, Provides> provides;
boolean open;
boolean automatic;
boolean synthetic;
Set<Requires> requires;
Set<Exports> exports;
Set<Opens> opens;
Set<String> packages;
Set<String> uses;
Set<Provides> provides;
Version version;
String mainClass;
String osName;
String osArch;
String osVersion;
String algorithm;
Map<String, String> hashes;
Map<String, byte[]> hashes;
Builder(String name, int reqs, int exports,
int provides, int packages) {
Builder(String name) {
this.name = name;
this.requires = reqs > 0 ? new HashSet<>(reqs) : Collections.emptySet();
this.exports = exports > 0 ? new HashSet<>(exports) : Collections.emptySet();
this.provides = provides > 0 ? new HashMap<>(provides) : Collections.emptyMap();
this.requires = Collections.emptySet();
this.exports = Collections.emptySet();
this.opens = Collections.emptySet();
this.provides = Collections.emptySet();
this.uses = Collections.emptySet();
}
/**
* Adds a module dependence with the given (and possibly empty) set
* of modifiers.
*/
public Builder requires(Set<Requires.Modifier> mods, String mn) {
requires.add(jlma.newRequires(Collections.unmodifiableSet(mods), mn));
Builder open(boolean value) {
this.open = value;
return this;
}
Builder automatic(boolean value) {
this.automatic = value;
return this;
}
Builder synthetic(boolean value) {
this.synthetic = value;
return this;
}
/**
* Adds a module dependence with an empty set of modifiers.
* Sets module exports.
*/
public Builder requires(String mn) {
requires.add(jlma.newRequires(Collections.emptySet(), mn));
public Builder exports(Exports[] exports) {
this.exports = Set.of(exports);
return this;
}
/**
* Adds a module dependence with the given modifier.
* Sets module opens.
*/
public Builder requires(Requires.Modifier mod, String mn) {
if (mod == Requires.Modifier.MANDATED) {
requires.add(jlma.newRequires(MANDATED, mn));
} else if (mod == Requires.Modifier.PUBLIC) {
requires.add(jlma.newRequires(PUBLIC, mn));
} else {
requires.add(jlma.newRequires(Collections.singleton(mod), mn));
}
public Builder opens(Opens[] opens) {
this.opens = Set.of(opens);
return this;
}
/**
* Sets module requires.
*/
public Builder requires(Requires[] requires) {
this.requires = Set.of(requires);
return this;
}
/**
* Adds a set of (possible empty) packages.
*/
public Builder packages(Set<String> packages) {
this.packages = packages;
return this;
}
@ -125,51 +198,10 @@ final class Builder {
}
/**
* Adds an export to a set of target modules.
* Sets module provides.
*/
public Builder exports(String pn, Set<String> targets) {
exports.add(jlma.newExports(pn, targets));
return this;
}
/**
* Adds an export to a target module.
*/
public Builder exports(String pn, String target) {
return exports(pn, Collections.singleton(target));
}
/**
* Adds an export.
*/
public Builder exports(String pn) {
exports.add(jlma.newExports(pn));
return this;
}
/**
* Provides service {@code st} with implementations {@code pcs}.
*/
public Builder provides(String st, Set<String> pcs) {
if (provides.containsKey(st))
throw new IllegalStateException("Providers of service "
+ st + " already declared");
provides.put(st, jlma.newProvides(st, pcs));
return this;
}
/**
* Provides service {@code st} with implementation {@code pc}.
*/
public Builder provides(String st, String pc) {
return provides(st, Collections.singleton(pc));
}
/**
* Adds a set of (possible empty) packages.
*/
public Builder packages(Set<String> packages) {
this.packages = packages;
public Builder provides(Provides[] provides) {
this.provides = Set.of(provides);
return this;
}
@ -253,7 +285,7 @@ final class Builder {
/**
* Sets the module hash for the given module name
*/
public Builder moduleHash(String mn, String hash) {
public Builder moduleHash(String mn, byte[] hash) {
if (hashes == null)
hashes = new HashMap<>();
@ -264,18 +296,20 @@ final class Builder {
/**
* Builds a {@code ModuleDescriptor} from the components.
*/
public ModuleDescriptor build() {
public ModuleDescriptor build(int hashCode) {
assert name != null;
ModuleHashes moduleHashes =
hashes != null ? new ModuleHashes(algorithm, hashes) : null;
return jlma.newModuleDescriptor(name,
false, // automatic
false, // assume not synthetic for now
open,
automatic,
synthetic,
requires,
uses,
exports,
opens,
uses,
provides,
version,
mainClass,
@ -283,6 +317,7 @@ final class Builder {
osArch,
osVersion,
packages,
moduleHashes);
moduleHashes,
hashCode);
}
}

@ -25,7 +25,6 @@
package jdk.internal.module;
public final class Checks {
private Checks() { }

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2016, 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
@ -27,17 +27,20 @@ package jdk.internal.module;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Requires.Modifier;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Version;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jdk.internal.misc.JavaLangModuleAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.org.objectweb.asm.Attribute;
import jdk.internal.org.objectweb.asm.ByteVector;
import jdk.internal.org.objectweb.asm.ClassReader;
@ -51,7 +54,7 @@ import static jdk.internal.module.ClassFileConstants.*;
* class file attributes in a module-info class file.
*/
class ClassFileAttributes {
public final class ClassFileAttributes {
private ClassFileAttributes() { }
@ -60,16 +63,18 @@ class ClassFileAttributes {
* // See lang-vm.html for details.
* }
*/
static class ModuleAttribute extends Attribute {
public static class ModuleAttribute extends Attribute {
private static final JavaLangModuleAccess JLMA
= SharedSecrets.getJavaLangModuleAccess();
private ModuleDescriptor descriptor;
ModuleAttribute(ModuleDescriptor descriptor) {
public ModuleAttribute(ModuleDescriptor descriptor) {
super(MODULE);
this.descriptor = descriptor;
}
ModuleAttribute() {
public ModuleAttribute() {
super(MODULE);
}
@ -81,27 +86,43 @@ class ClassFileAttributes {
int codeOff,
Label[] labels)
{
ModuleDescriptor.Builder builder
= new ModuleDescriptor.Builder("xyzzy"); // Name never used
ModuleAttribute attr = new ModuleAttribute();
// module_name
String mn = cr.readUTF8(off, buf).replace('/', '.');
off += 2;
// module_flags
int module_flags = cr.readUnsignedShort(off);
boolean open = ((module_flags & ACC_OPEN) != 0);
off += 2;
ModuleDescriptor.Builder builder;
if (open) {
builder = JLMA.newOpenModuleBuilder(mn, false);
} else {
builder = JLMA.newModuleBuilder(mn, false);
}
// requires_count and requires[requires_count]
int requires_count = cr.readUnsignedShort(off);
off += 2;
for (int i=0; i<requires_count; i++) {
String dn = cr.readUTF8(off, buf);
String dn = cr.readUTF8(off, buf).replace('/', '.');
int flags = cr.readUnsignedShort(off + 2);
Set<Modifier> mods;
Set<Requires.Modifier> mods;
if (flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_PUBLIC) != 0)
mods.add(Modifier.PUBLIC);
if ((flags & ACC_TRANSITIVE) != 0)
mods.add(Requires.Modifier.TRANSITIVE);
if ((flags & ACC_STATIC_PHASE) != 0)
mods.add(Requires.Modifier.STATIC);
if ((flags & ACC_SYNTHETIC) != 0)
mods.add(Modifier.SYNTHETIC);
mods.add(Requires.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
mods.add(Modifier.MANDATED);
mods.add(Requires.Modifier.MANDATED);
}
builder.requires(mods, dn);
off += 4;
@ -113,18 +134,70 @@ class ClassFileAttributes {
if (exports_count > 0) {
for (int i=0; i<exports_count; i++) {
String pkg = cr.readUTF8(off, buf).replace('/', '.');
int exports_to_count = cr.readUnsignedShort(off+2);
off += 4;
off += 2;
int flags = cr.readUnsignedShort(off);
off += 2;
Set<Exports.Modifier> mods;
if (flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_SYNTHETIC) != 0)
mods.add(Exports.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
mods.add(Exports.Modifier.MANDATED);
}
int exports_to_count = cr.readUnsignedShort(off);
off += 2;
if (exports_to_count > 0) {
Set<String> targets = new HashSet<>();
for (int j=0; j<exports_to_count; j++) {
String t = cr.readUTF8(off, buf);
String t = cr.readUTF8(off, buf).replace('/', '.');
off += 2;
targets.add(t);
}
builder.exports(pkg, targets);
builder.exports(mods, pkg, targets);
} else {
builder.exports(pkg);
builder.exports(mods, pkg);
}
}
}
// opens_count and opens[opens_count]
int open_count = cr.readUnsignedShort(off);
off += 2;
if (open_count > 0) {
for (int i=0; i<open_count; i++) {
String pkg = cr.readUTF8(off, buf).replace('/', '.');
off += 2;
int flags = cr.readUnsignedShort(off);
off += 2;
Set<Opens.Modifier> mods;
if (flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
if ((flags & ACC_SYNTHETIC) != 0)
mods.add(Opens.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
mods.add(Opens.Modifier.MANDATED);
}
int opens_to_count = cr.readUnsignedShort(off);
off += 2;
if (opens_to_count > 0) {
Set<String> targets = new HashSet<>();
for (int j=0; j<opens_to_count; j++) {
String t = cr.readUTF8(off, buf).replace('/', '.');
off += 2;
targets.add(t);
}
builder.opens(mods, pkg, targets);
} else {
builder.opens(mods, pkg);
}
}
}
@ -144,15 +217,19 @@ class ClassFileAttributes {
int provides_count = cr.readUnsignedShort(off);
off += 2;
if (provides_count > 0) {
Map<String, Set<String>> provides = new HashMap<>();
for (int i=0; i<provides_count; i++) {
String sn = cr.readClass(off, buf).replace('/', '.');
String cn = cr.readClass(off + 2, buf).replace('/', '.');
provides.computeIfAbsent(sn, k -> new LinkedHashSet<>()).add(cn);
off += 4;
String service = cr.readClass(off, buf).replace('/', '.');
off += 2;
int with_count = cr.readUnsignedShort(off);
off += 2;
List<String> providers = new ArrayList<>();
for (int j=0; j<with_count; j++) {
String cn = cr.readClass(off, buf).replace('/', '.');
off += 2;
providers.add(cn);
}
builder.provides(service, providers);
}
provides.entrySet().forEach(e -> builder.provides(e.getKey(),
e.getValue()));
}
attr.descriptor = builder.build();
@ -169,6 +246,19 @@ class ClassFileAttributes {
assert descriptor != null;
ByteVector attr = new ByteVector();
// module_name
String mn = descriptor.name();
int module_name_index = cw.newUTF8(mn.replace('.', '/'));
attr.putShort(module_name_index);
// module_flags
int module_flags = 0;
if (descriptor.isOpen())
module_flags |= ACC_OPEN;
if (descriptor.isSynthetic())
module_flags |= ACC_SYNTHETIC;
attr.putShort(module_flags);
// requires_count
attr.putShort(descriptor.requires().size());
@ -176,32 +266,61 @@ class ClassFileAttributes {
for (Requires md : descriptor.requires()) {
String dn = md.name();
int flags = 0;
if (md.modifiers().contains(Modifier.PUBLIC))
flags |= ACC_PUBLIC;
if (md.modifiers().contains(Modifier.SYNTHETIC))
if (md.modifiers().contains(Requires.Modifier.TRANSITIVE))
flags |= ACC_TRANSITIVE;
if (md.modifiers().contains(Requires.Modifier.STATIC))
flags |= ACC_STATIC_PHASE;
if (md.modifiers().contains(Requires.Modifier.SYNTHETIC))
flags |= ACC_SYNTHETIC;
if (md.modifiers().contains(Modifier.MANDATED))
if (md.modifiers().contains(Requires.Modifier.MANDATED))
flags |= ACC_MANDATED;
int index = cw.newUTF8(dn);
int index = cw.newUTF8(dn.replace('.', '/'));
attr.putShort(index);
attr.putShort(flags);
}
// exports_count and exports[exports_count];
if (descriptor.exports().isEmpty()) {
attr.putShort(0);
} else {
attr.putShort(descriptor.exports().size());
for (Exports e : descriptor.exports()) {
String pkg = e.source().replace('.', '/');
attr.putShort(cw.newUTF8(pkg));
if (e.isQualified()) {
Set<String> ts = e.targets();
attr.putShort(ts.size());
ts.forEach(t -> attr.putShort(cw.newUTF8(t)));
} else {
attr.putShort(0);
}
attr.putShort(descriptor.exports().size());
for (Exports e : descriptor.exports()) {
String pkg = e.source().replace('.', '/');
attr.putShort(cw.newUTF8(pkg));
int flags = 0;
if (e.modifiers().contains(Exports.Modifier.SYNTHETIC))
flags |= ACC_SYNTHETIC;
if (e.modifiers().contains(Exports.Modifier.MANDATED))
flags |= ACC_MANDATED;
attr.putShort(flags);
if (e.isQualified()) {
Set<String> ts = e.targets();
attr.putShort(ts.size());
ts.forEach(t -> attr.putShort(cw.newUTF8(t.replace('.', '/'))));
} else {
attr.putShort(0);
}
}
// opens_counts and opens[opens_counts]
attr.putShort(descriptor.opens().size());
for (Opens obj : descriptor.opens()) {
String pkg = obj.source().replace('.', '/');
attr.putShort(cw.newUTF8(pkg));
int flags = 0;
if (obj.modifiers().contains(Opens.Modifier.SYNTHETIC))
flags |= ACC_SYNTHETIC;
if (obj.modifiers().contains(Opens.Modifier.MANDATED))
flags |= ACC_MANDATED;
attr.putShort(flags);
if (obj.isQualified()) {
Set<String> ts = obj.targets();
attr.putShort(ts.size());
ts.forEach(t -> attr.putShort(cw.newUTF8(t.replace('.', '/'))));
} else {
attr.putShort(0);
}
}
@ -221,14 +340,13 @@ class ClassFileAttributes {
if (descriptor.provides().isEmpty()) {
attr.putShort(0);
} else {
int count = descriptor.provides().values()
.stream().mapToInt(ps -> ps.providers().size()).sum();
attr.putShort(count);
for (Provides p : descriptor.provides().values()) {
attr.putShort(descriptor.provides().size());
for (Provides p : descriptor.provides()) {
String service = p.service().replace('.', '/');
int index = cw.newClass(service);
attr.putShort(cw.newClass(service));
int with_count = p.providers().size();
attr.putShort(with_count);
for (String provider : p.providers()) {
attr.putShort(index);
attr.putShort(cw.newClass(provider.replace('.', '/')));
}
}
@ -239,44 +357,13 @@ class ClassFileAttributes {
}
/**
* Synthetic attribute.
*/
static class SyntheticAttribute extends Attribute {
SyntheticAttribute() {
super(SYNTHETIC);
}
@Override
protected Attribute read(ClassReader cr,
int off,
int len,
char[] buf,
int codeOff,
Label[] labels)
{
return new SyntheticAttribute();
}
@Override
protected ByteVector write(ClassWriter cw,
byte[] code,
int len,
int maxStack,
int maxLocals)
{
ByteVector attr = new ByteVector();
return attr;
}
}
/**
* ConcealedPackages attribute.
* ModulePackages attribute.
*
* <pre> {@code
*
* ConcealedPackages_attribute {
* ModulePackages_attribute {
* // index to CONSTANT_utf8_info structure in constant pool representing
* // the string "ConcealedPackages"
* // the string "ModulePackages"
* u2 attribute_name_index;
* u4 attribute_length;
*
@ -288,15 +375,15 @@ class ClassFileAttributes {
*
* }</pre>
*/
static class ConcealedPackagesAttribute extends Attribute {
public static class ModulePackagesAttribute extends Attribute {
private final Set<String> packages;
ConcealedPackagesAttribute(Set<String> packages) {
super(CONCEALED_PACKAGES);
public ModulePackagesAttribute(Set<String> packages) {
super(MODULE_PACKAGES);
this.packages = packages;
}
ConcealedPackagesAttribute() {
public ModulePackagesAttribute() {
this(null);
}
@ -320,7 +407,7 @@ class ClassFileAttributes {
off += 2;
}
return new ConcealedPackagesAttribute(packages);
return new ModulePackagesAttribute(packages);
}
@Override
@ -348,13 +435,13 @@ class ClassFileAttributes {
}
/**
* Version attribute.
* ModuleVersion attribute.
*
* <pre> {@code
*
* Version_attribute {
* ModuleVersion_attribute {
* // index to CONSTANT_utf8_info structure in constant pool representing
* // the string "Version"
* // the string "ModuleVersion"
* u2 attribute_name_index;
* u4 attribute_length;
*
@ -364,15 +451,15 @@ class ClassFileAttributes {
*
* } </pre>
*/
static class VersionAttribute extends Attribute {
public static class ModuleVersionAttribute extends Attribute {
private final Version version;
VersionAttribute(Version version) {
super(VERSION);
public ModuleVersionAttribute(Version version) {
super(MODULE_VERSION);
this.version = version;
}
VersionAttribute() {
public ModuleVersionAttribute() {
this(null);
}
@ -385,7 +472,7 @@ class ClassFileAttributes {
Label[] labels)
{
String value = cr.readUTF8(off, buf);
return new VersionAttribute(Version.parse(value));
return new ModuleVersionAttribute(Version.parse(value));
}
@Override
@ -403,13 +490,13 @@ class ClassFileAttributes {
}
/**
* MainClass attribute.
* ModuleMainClass attribute.
*
* <pre> {@code
*
* MainClass_attribute {
* // index to CONSTANT_utf8_info structure in constant pool representing
* // the string "MainClass"
* // the string "ModuleMainClass"
* u2 attribute_name_index;
* u4 attribute_length;
*
@ -419,15 +506,15 @@ class ClassFileAttributes {
*
* } </pre>
*/
static class MainClassAttribute extends Attribute {
public static class ModuleMainClassAttribute extends Attribute {
private final String mainClass;
MainClassAttribute(String mainClass) {
super(MAIN_CLASS);
public ModuleMainClassAttribute(String mainClass) {
super(MODULE_MAIN_CLASS);
this.mainClass = mainClass;
}
MainClassAttribute() {
public ModuleMainClassAttribute() {
this(null);
}
@ -440,7 +527,7 @@ class ClassFileAttributes {
Label[] labels)
{
String value = cr.readClass(off, buf);
return new MainClassAttribute(value);
return new ModuleMainClassAttribute(value);
}
@Override
@ -458,13 +545,13 @@ class ClassFileAttributes {
}
/**
* TargetPlatform attribute.
* ModuleTarget attribute.
*
* <pre> {@code
*
* TargetPlatform_attribute {
* // index to CONSTANT_utf8_info structure in constant pool representing
* // the string "TargetPlatform"
* // the string "ModuleTarget"
* u2 attribute_name_index;
* u4 attribute_length;
*
@ -478,19 +565,19 @@ class ClassFileAttributes {
*
* } </pre>
*/
static class TargetPlatformAttribute extends Attribute {
public static class ModuleTargetAttribute extends Attribute {
private final String osName;
private final String osArch;
private final String osVersion;
TargetPlatformAttribute(String osName, String osArch, String osVersion) {
super(TARGET_PLATFORM);
public ModuleTargetAttribute(String osName, String osArch, String osVersion) {
super(MODULE_TARGET);
this.osName = osName;
this.osArch = osArch;
this.osVersion = osVersion;
}
TargetPlatformAttribute() {
public ModuleTargetAttribute() {
this(null, null, null);
}
@ -522,7 +609,7 @@ class ClassFileAttributes {
osVersion = cr.readUTF8(off, buf);
off += 2;
return new TargetPlatformAttribute(osName, osArch, osVersion);
return new ModuleTargetAttribute(osName, osArch, osVersion);
}
@Override
@ -554,39 +641,37 @@ class ClassFileAttributes {
}
/**
* Hashes attribute.
* ModuleHashes attribute.
*
* <pre> {@code
*
* Hashes_attribute {
* ModuleHashes_attribute {
* // index to CONSTANT_utf8_info structure in constant pool representing
* // the string "Hashes"
* // the string "ModuleHashes"
* u2 attribute_name_index;
* u4 attribute_length;
*
* // index to CONSTANT_CONSTANT_utf8_info structure with algorithm name
* // index to CONSTANT_utf8_info structure with algorithm name
* u2 algorithm_index;
*
* // the number of entries in the hashes table
* u2 hash_count;
* { u2 requires_index
* u2 hash_index;
* } hashes[hash_count];
* u2 hashes_count;
* { u2 module_name_index
* u2 hash_length;
* u1 hash[hash_length];
* } hashes[hashes_count];
*
* } </pre>
*
* @apiNote For now the hash is stored in base64 as a UTF-8 string, an
* alternative is to store it as an array of u1.
*/
static class HashesAttribute extends Attribute {
static class ModuleHashesAttribute extends Attribute {
private final ModuleHashes hashes;
HashesAttribute(ModuleHashes hashes) {
super(HASHES);
ModuleHashesAttribute(ModuleHashes hashes) {
super(MODULE_HASHES);
this.hashes = hashes;
}
HashesAttribute() {
ModuleHashesAttribute() {
this(null);
}
@ -601,21 +686,28 @@ class ClassFileAttributes {
String algorithm = cr.readUTF8(off, buf);
off += 2;
int hash_count = cr.readUnsignedShort(off);
int hashes_count = cr.readUnsignedShort(off);
off += 2;
Map<String, String> map = new HashMap<>();
for (int i=0; i<hash_count; i++) {
String dn = cr.readUTF8(off, buf);
Map<String, byte[]> map = new HashMap<>();
for (int i=0; i<hashes_count; i++) {
String mn = cr.readUTF8(off, buf).replace('/', '.');
off += 2;
String hash = cr.readUTF8(off, buf);
int hash_length = cr.readUnsignedShort(off);
off += 2;
map.put(dn, hash);
byte[] hash = new byte[hash_length];
for (int j=0; j<hash_length; j++) {
hash[j] = (byte) (0xff & cr.readByte(off+j));
}
off += hash_length;
map.put(mn, hash);
}
ModuleHashes hashes = new ModuleHashes(algorithm, map);
return new HashesAttribute(hashes);
return new ModuleHashesAttribute(hashes);
}
@Override
@ -633,11 +725,15 @@ class ClassFileAttributes {
Set<String> names = hashes.names();
attr.putShort(names.size());
for (String dn : names) {
String hash = hashes.hashFor(dn);
for (String mn : names) {
byte[] hash = hashes.hashFor(mn);
assert hash != null;
attr.putShort(cw.newUTF8(dn));
attr.putShort(cw.newUTF8(hash));
attr.putShort(cw.newUTF8(mn.replace('.', '/')));
attr.putShort(hash.length);
for (byte b: hash) {
attr.putByte(b);
}
}
return attr;

@ -35,19 +35,20 @@ public class ClassFileConstants {
// Attribute names
public static final String MODULE = "Module";
public static final String SOURCE_FILE = "SourceFile";
public static final String SYNTHETIC = "Synthetic";
public static final String SDE = "SourceDebugExtension";
public static final String CONCEALED_PACKAGES = "ConcealedPackages";
public static final String VERSION = "Version";
public static final String MAIN_CLASS = "MainClass";
public static final String TARGET_PLATFORM = "TargetPlatform";
public static final String HASHES = "Hashes";
public static final String MODULE_PACKAGES = "ModulePackages";
public static final String MODULE_VERSION = "ModuleVersion";
public static final String MODULE_MAIN_CLASS = "ModuleMainClass";
public static final String MODULE_TARGET = "ModuleTarget";
public static final String MODULE_HASHES = "ModuleHashes";
// access and requires flags
public static final int ACC_MODULE = 0x8000;
public static final int ACC_PUBLIC = 0x0020;
public static final int ACC_SYNTHETIC = 0x1000;
public static final int ACC_MANDATED = 0x8000;
// access, requires, exports, and opens flags
public static final int ACC_MODULE = 0x8000;
public static final int ACC_OPEN = 0x0020;
public static final int ACC_TRANSITIVE = 0x0010;
public static final int ACC_STATIC_PHASE = 0x0020;
public static final int ACC_SYNTHETIC = 0x1000;
public static final int ACC_MANDATED = 0x8000;
}

@ -37,9 +37,11 @@ import java.lang.reflect.Module;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@ -81,9 +83,19 @@ public final class ModuleBootstrap {
// the token for "all modules on the module path"
private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
// The ModulePatcher for the initial configuration
private static final ModulePatcher patcher = initModulePatcher();
// ModuleFinder for the initial configuration
private static ModuleFinder initialFinder;
/**
* Returns the ModulePatcher for the initial configuration.
*/
public static ModulePatcher patcher() {
return patcher;
}
/**
* Returns the ModuleFinder for the initial configuration
*/
@ -101,7 +113,7 @@ public final class ModuleBootstrap {
long t0 = System.nanoTime();
// system modules
// system modules (may be patched)
ModuleFinder systemModules = ModuleFinder.ofSystem();
PerfCounters.systemModulesTime.addElapsedTimeFrom(t0);
@ -247,7 +259,7 @@ public final class ModuleBootstrap {
if (baseUri.getScheme().equals("jrt") // toLowerCase not needed here
&& (upgradeModulePath == null)
&& (appModulePath == null)
&& (!ModulePatcher.isBootLayerPatched())) {
&& (patcher.isEmpty())) {
needPostResolutionChecks = false;
}
@ -314,9 +326,9 @@ public final class ModuleBootstrap {
PerfCounters.loadModulesTime.addElapsedTimeFrom(t5);
// --add-reads and --add-exports
// --add-reads, -add-exports/-add-opens
addExtraReads(bootLayer);
addExtraExports(bootLayer);
addExtraExportsAndOpens(bootLayer);
// total time to initialize
PerfCounters.bootstrapTime.addElapsedTimeFrom(t0);
@ -389,6 +401,18 @@ public final class ModuleBootstrap {
}
}
/**
* Initialize the module patcher for the initial configuration passed on the
* value of the --patch-module options.
*/
private static ModulePatcher initModulePatcher() {
Map<String, List<String>> map = decode("jdk.module.patch.",
File.pathSeparator,
false);
return new ModulePatcher(map);
}
/**
* Returns the set of module names specified via --add-modules options
* on the command line
@ -408,7 +432,6 @@ public final class ModuleBootstrap {
for (String s : value.split(",")) {
if (s.length() > 0) modules.add(s);
}
index++;
value = getAndRemoveProperty(prefix + index);
}
@ -423,9 +446,11 @@ public final class ModuleBootstrap {
private static void addExtraReads(Layer bootLayer) {
// decode the command line options
Map<String, Set<String>> map = decode("jdk.module.addreads.");
Map<String, List<String>> map = decode("jdk.module.addreads.");
if (map.isEmpty())
return;
for (Map.Entry<String, Set<String>> e : map.entrySet()) {
for (Map.Entry<String, List<String>> e : map.entrySet()) {
// the key is $MODULE
String mn = e.getKey();
@ -448,22 +473,36 @@ public final class ModuleBootstrap {
warn("Unknown module: " + name);
}
}
}
}
}
/**
* Process the --add-exports options to add any additional read edges that
* are specified on the command-line.
* Process the --add-exports and --add-opens options to export/open
* additional packages specified on the command-line.
*/
private static void addExtraExports(Layer bootLayer) {
private static void addExtraExportsAndOpens(Layer bootLayer) {
// decode the command line options
Map<String, Set<String>> map = decode("jdk.module.addexports.");
// --add-exports
String prefix = "jdk.module.addexports.";
Map<String, List<String>> extraExports = decode(prefix);
if (!extraExports.isEmpty()) {
addExtraExportsOrOpens(bootLayer, extraExports, false);
}
for (Map.Entry<String, Set<String>> e : map.entrySet()) {
// --add-opens
prefix = "jdk.module.addopens.";
Map<String, List<String>> extraOpens = decode(prefix);
if (!extraOpens.isEmpty()) {
addExtraExportsOrOpens(bootLayer, extraOpens, true);
}
}
private static void addExtraExportsOrOpens(Layer bootLayer,
Map<String, List<String>> map,
boolean opens)
{
for (Map.Entry<String, List<String>> e : map.entrySet()) {
// the key is $MODULE/$PACKAGE
String key = e.getKey();
@ -507,28 +546,40 @@ public final class ModuleBootstrap {
}
}
if (allUnnamed) {
Modules.addExportsToAllUnnamed(m, pn);
if (opens) {
Modules.addOpensToAllUnnamed(m, pn);
} else {
Modules.addExportsToAllUnnamed(m, pn);
}
} else {
Modules.addExports(m, pn, other);
if (opens) {
Modules.addOpens(m, pn, other);
} else {
Modules.addExports(m, pn, other);
}
}
}
}
}
/**
* Decodes the values of --add-reads or --add-exports options
* Decodes the values of --add-reads, -add-exports, --add-opens or
* --patch-modules options that are encoded in system properties.
*
* The format of the options is: $KEY=$MODULE(,$MODULE)*
* @param prefix the system property prefix
* @praam regex the regex for splitting the RHS of the option value
*/
private static Map<String, Set<String>> decode(String prefix) {
private static Map<String, List<String>> decode(String prefix,
String regex,
boolean allowDuplicates) {
int index = 0;
// the system property is removed after decoding
String value = getAndRemoveProperty(prefix + index);
if (value == null)
return Collections.emptyMap();
Map<String, Set<String>> map = new HashMap<>();
Map<String, List<String>> map = new HashMap<>();
while (value != null) {
@ -545,8 +596,11 @@ public final class ModuleBootstrap {
if (rhs.isEmpty())
fail("Unable to parse: " + value);
Set<String> values = map.computeIfAbsent(key, k -> new HashSet<>());
for (String s : rhs.split(",")) {
// value is <module>(,<module>)* or <file>(<pathsep><file>)*
if (!allowDuplicates && map.containsKey(key))
fail(key + " specified more than once");
List<String> values = map.computeIfAbsent(key, k -> new ArrayList<>());
for (String s : rhs.split(regex)) {
if (s.length() > 0) values.add(s);
}
@ -557,6 +611,14 @@ public final class ModuleBootstrap {
return map;
}
/**
* Decodes the values of --add-reads, -add-exports or --add-opens
* which use the "," to separate the RHS of the option value.
*/
private static Map<String, List<String>> decode(String prefix) {
return decode(prefix, ",", true);
}
/**
* Gets and remove the named system property
*/

@ -32,7 +32,6 @@ import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -45,23 +44,23 @@ import java.util.Set;
public final class ModuleHashes {
/**
* A supplier of an encoded message digest.
* A supplier of a message digest.
*/
public static interface HashSupplier {
String generate(String algorithm);
byte[] generate(String algorithm);
}
private final String algorithm;
private final Map<String, String> nameToHash;
private final Map<String, byte[]> nameToHash;
/**
* Creates a {@code ModuleHashes}.
*
* @param algorithm the algorithm used to create the hashes
* @param nameToHash the map of module name to hash value (in string form)
* @param nameToHash the map of module name to hash value
*/
public ModuleHashes(String algorithm, Map<String, String> nameToHash) {
public ModuleHashes(String algorithm, Map<String, byte[]> nameToHash) {
this.algorithm = algorithm;
this.nameToHash = Collections.unmodifiableMap(nameToHash);
}
@ -81,28 +80,28 @@ public final class ModuleHashes {
}
/**
* Returns the hash string for the given module name, {@code null}
* Returns the hash for the given module name, {@code null}
* if there is no hash recorded for the module.
*/
public String hashFor(String mn) {
public byte[] hashFor(String mn) {
return nameToHash.get(mn);
}
/**
* Returns unmodifiable map of module name to hash string.
* Returns unmodifiable map of module name to hash
*/
public Map<String, String> hashes() {
public Map<String, byte[]> hashes() {
return nameToHash;
}
/**
* Computes the hash for the given file with the given message digest
* algorithm. Returns the results a base64-encoded String.
* algorithm.
*
* @throws UncheckedIOException if an I/O error occurs
* @throws RuntimeException if the algorithm is not available
*/
public static String computeHashAsString(Path file, String algorithm) {
public static byte[] computeHash(Path file, String algorithm) {
try {
MessageDigest md = MessageDigest.getInstance(algorithm);
@ -118,8 +117,7 @@ public final class ModuleHashes {
}
}
byte[] bytes = md.digest();
return Base64.getEncoder().encodeToString(bytes);
return md.digest();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (IOException ioe) {
@ -133,14 +131,14 @@ public final class ModuleHashes {
* the entry name, typically the module name. The map value is the file
* path to the entry (module artifact).
*
* @return ModuleHashes encapsulate the hashes
* @return ModuleHashes that encapsulates the hashes
*/
public static ModuleHashes generate(Map<String, Path> map, String algorithm) {
Map<String, String> nameToHash = new HashMap<>();
Map<String, byte[]> nameToHash = new HashMap<>();
for (Map.Entry<String, Path> entry: map.entrySet()) {
String name = entry.getKey();
Path path = entry.getValue();
nameToHash.put(name, computeHashAsString(path, algorithm));
nameToHash.put(name, computeHash(path, algorithm));
}
return new ModuleHashes(algorithm, nameToHash);
}

@ -53,8 +53,8 @@ public final class ModuleInfoExtender {
// the input stream to read the original module-info.class
private final InputStream in;
// the packages in the ConcealedPackages attribute
private Set<String> conceals;
// the packages in the Packages attribute
private Set<String> packages;
// the value of the Version attribute
private Version version;
@ -75,10 +75,10 @@ public final class ModuleInfoExtender {
}
/**
* Sets the set of packages for the ConcealedPackages attribute
* Sets the set of packages for the Packages attribute
*/
public ModuleInfoExtender conceals(Set<String> packages) {
this.conceals = Collections.unmodifiableSet(packages);
public ModuleInfoExtender packages(Set<String> packages) {
this.packages = Collections.unmodifiableSet(packages);
return this;
}
@ -181,26 +181,26 @@ public final class ModuleInfoExtender {
ClassReader cr = new ClassReader(in);
if (conceals != null)
cv.addAttribute(new ConcealedPackagesAttribute(conceals));
if (packages != null)
cv.addAttribute(new ModulePackagesAttribute(packages));
if (version != null)
cv.addAttribute(new VersionAttribute(version));
cv.addAttribute(new ModuleVersionAttribute(version));
if (mainClass != null)
cv.addAttribute(new MainClassAttribute(mainClass));
cv.addAttribute(new ModuleMainClassAttribute(mainClass));
if (osName != null || osArch != null || osVersion != null)
cv.addAttribute(new TargetPlatformAttribute(osName, osArch, osVersion));
cv.addAttribute(new ModuleTargetAttribute(osName, osArch, osVersion));
if (hashes != null)
cv.addAttribute(new HashesAttribute(hashes));
cv.addAttribute(new ModuleHashesAttribute(hashes));
List<Attribute> attrs = new ArrayList<>();
// prototypes of attributes that should be parsed
attrs.add(new ModuleAttribute());
attrs.add(new ConcealedPackagesAttribute());
attrs.add(new VersionAttribute());
attrs.add(new MainClassAttribute());
attrs.add(new TargetPlatformAttribute());
attrs.add(new HashesAttribute());
attrs.add(new ModulePackagesAttribute());
attrs.add(new ModuleVersionAttribute());
attrs.add(new ModuleMainClassAttribute());
attrs.add(new ModuleTargetAttribute());
attrs.add(new ModuleHashesAttribute());
cr.accept(cv, attrs.toArray(new Attribute[0]), 0);

@ -27,9 +27,8 @@ package jdk.internal.module;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Version;
import java.nio.ByteBuffer;
import java.util.Optional;
import java.util.stream.Stream;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;
@ -52,31 +51,28 @@ public final class ModuleInfoWriter {
private static byte[] toModuleInfo(ModuleDescriptor md) {
ClassWriter cw = new ClassWriter(0);
String name = md.name().replace('.', '/') + "/module-info";
cw.visit(Opcodes.V1_9, ACC_MODULE, name, null, null, null);
cw.visit(Opcodes.V1_9, ACC_MODULE, null, null, null, null);
cw.visitAttribute(new ModuleAttribute(md));
// for tests: write the ConcealedPackages attribute when there are non-exported packages
long nExportedPackages = md.exports().stream()
.map(ModuleDescriptor.Exports::source)
.distinct()
.count();
if (md.packages().size() > nExportedPackages)
cw.visitAttribute(new ConcealedPackagesAttribute(md.packages()));
// for tests: write the Packages attribute when there are packages that
// aren't exported or open
Stream<String> exported = md.exports().stream()
.map(ModuleDescriptor.Exports::source);
Stream<String> open = md.opens().stream()
.map(ModuleDescriptor.Opens::source);
long exportedOrOpen = Stream.concat(exported, open).distinct().count();
if (md.packages().size() > exportedOrOpen)
cw.visitAttribute(new ModulePackagesAttribute(md.packages()));
md.version().ifPresent(v -> cw.visitAttribute(new VersionAttribute(v)));
md.mainClass().ifPresent(mc -> cw.visitAttribute(new MainClassAttribute(mc)));
md.version().ifPresent(v -> cw.visitAttribute(new ModuleVersionAttribute(v)));
md.mainClass().ifPresent(mc -> cw.visitAttribute(new ModuleMainClassAttribute(mc)));
// write the TargetPlatform attribute if have any of OS name/arch/version
String osName = md.osName().orElse(null);
String osArch = md.osArch().orElse(null);
String osVersion = md.osVersion().orElse(null);
if (osName != null || osArch != null || osVersion != null) {
cw.visitAttribute(new TargetPlatformAttribute(osName,
osArch,
osVersion));
cw.visitAttribute(new ModuleTargetAttribute(osName, osArch, osVersion));
}
cw.visitEnd();

@ -50,6 +50,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.loader.Resource;
@ -59,7 +60,7 @@ import sun.net.www.ParseUtil;
/**
* Provides support for patching modules in the boot layer with --patch-module.
* Provides support for patching modules, mostly the boot layer.
*/
public final class ModulePatcher {
@ -67,89 +68,46 @@ public final class ModulePatcher {
private static final JavaLangModuleAccess JLMA
= SharedSecrets.getJavaLangModuleAccess();
// the prefix of the system properties that encode the value of --patch-module
private static final String PATCH_PROPERTY_PREFIX = "jdk.module.patch.";
// module name -> sequence of patches (directories or JAR files)
private static final Map<String, List<Path>> PATCH_MAP = decodeProperties();
private ModulePatcher() { }
private final Map<String, List<Path>> map;
/**
* Decodes the values of --patch-module options, returning a Map of module
* name to list of file paths.
*
* @throws IllegalArgumentException if the the module name is missing or
* --patch-module is used more than once to patch the same module
* Initialize the module patcher with the given map. The map key is
* the module name, the value is a list of path strings.
*/
private static Map<String, List<Path>> decodeProperties() {
int index = 0;
String value = getAndRemoveProperty(PATCH_PROPERTY_PREFIX + index);
if (value == null)
return Collections.emptyMap(); // --patch-module not specified
Map<String, List<Path>> map = new HashMap<>();
while (value != null) {
// <module>=<file>(:<file>)*
int pos = value.indexOf('=');
if (pos == -1)
throwIAE("Unable to parse: " + value);
if (pos == 0)
throwIAE("Missing module name: " + value);
String mn = value.substring(0, pos);
List<Path> list = map.get(mn);
if (list != null)
throwIAE("Module " + mn + " specified more than once");
list = new ArrayList<>();
map.put(mn, list);
String paths = value.substring(pos+1);
for (String path : paths.split(File.pathSeparator)) {
if (!path.isEmpty()) {
list.add(Paths.get(path));
}
public ModulePatcher(Map<String, List<String>> input) {
if (input.isEmpty()) {
this.map = Collections.emptyMap();
} else {
Map<String, List<Path>> map = new HashMap<>();
for (Map.Entry<String, List<String>> e : input.entrySet()) {
String mn = e.getKey();
List<Path> paths = e.getValue().stream()
.map(Paths::get)
.collect(Collectors.toList());
map.put(mn, paths);
}
index++;
value = getAndRemoveProperty(PATCH_PROPERTY_PREFIX + index);
this.map = map;
}
return map;
}
/**
* Returns {@code true} is --patch-module is specified to patch modules
* in the boot layer.
*/
static boolean isBootLayerPatched() {
return !PATCH_MAP.isEmpty();
}
/**
* Returns a module reference that interposes on the given module if
* needed. If there are no patches for the given module then the module
* reference is simply returned. Otherwise the patches for the module
* are scanned (to find any new concealed packages) and a new module
* reference is returned.
* are scanned (to find any new packages) and a new module reference is
* returned.
*
* @throws UncheckedIOException if an I/O error is detected
*/
public static ModuleReference interposeIfNeeded(ModuleReference mref) {
public ModuleReference patchIfNeeded(ModuleReference mref) {
// if there are no patches for the module then nothing to do
ModuleDescriptor descriptor = mref.descriptor();
String mn = descriptor.name();
// if there are no patches for the module then nothing to do
List<Path> paths = PATCH_MAP.get(mn);
List<Path> paths = map.get(mn);
if (paths == null)
return mref;
// scan the JAR file or directory tree to get the set of packages
Set<String> packages = new HashSet<>();
try {
@ -197,6 +155,13 @@ public final class ModulePatcher {
}
/**
* Returns true is this module patcher has no patches.
*/
public boolean isEmpty() {
return map.isEmpty();
}
/**
* A ModuleReader that reads resources from a patched module.
@ -568,13 +533,6 @@ public final class ModulePatcher {
}
}
/**
* Gets and remove the named system property
*/
private static String getAndRemoveProperty(String key) {
return (String)System.getProperties().remove(key);
}
/**
* Derives a package name from the name of an entry in a JAR file.
*/
@ -593,9 +551,4 @@ public final class ModulePatcher {
System.err.println("WARNING: " + e + " ignored in patch: " + file);
return "";
}
private static void throwIAE(String msg) {
throw new IllegalArgumentException(msg);
}
}

@ -26,8 +26,11 @@
package jdk.internal.module;
import java.lang.module.ModuleDescriptor;
import java.lang.reflect.Layer;
import java.lang.reflect.Module;
import java.net.URI;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Set;
import jdk.internal.loader.BootLoader;
@ -35,7 +38,6 @@ import jdk.internal.loader.ClassLoaders;
import jdk.internal.misc.JavaLangReflectModuleAccess;
import jdk.internal.misc.SharedSecrets;
/**
* A helper class to allow JDK classes create dynamic modules and to update
* modules, exports and the readability graph. It is also invoked by the VM
@ -53,7 +55,6 @@ public class Modules {
private static final JavaLangReflectModuleAccess JLRMA
= SharedSecrets.getJavaLangReflectModuleAccess();
/**
* Creates a new Module. The module has the given ModuleDescriptor and
* is defined to the given class loader.
@ -72,7 +73,7 @@ public class Modules {
/**
* Define a new module to the VM. The module has the given set of
* concealed packages and is defined to the given class loader.
* packages and is defined to the given class loader.
*
* The resulting Module is in a larval state in that it does not not read
* any other module and does not have any exports.
@ -81,8 +82,9 @@ public class Modules {
String name,
Set<String> packages)
{
ModuleDescriptor descriptor
= new ModuleDescriptor.Builder(name).conceals(packages).build();
ModuleDescriptor descriptor = ModuleDescriptor.module(name)
.contains(packages)
.build();
return JLRMA.defineModule(loader, descriptor, null);
}
@ -104,12 +106,20 @@ public class Modules {
/**
* Updates module m1 to export a package to module m2.
* Same as m1.addExports(pkg, m2) but without a caller check.
* Same as m1.addExports(pn, m2) but without a caller check.
*/
public static void addExports(Module m1, String pn, Module m2) {
JLRMA.addExports(m1, pn, m2);
}
/**
* Updates module m1 to open a package to module m2.
* Same as m1.addOpens(pn, m2) but without a caller check.
*/
public static void addOpens(Module m1, String pn, Module m2) {
JLRMA.addOpens(m1, pn, m2);
}
/**
* Updates a module m to export a package to all modules.
*/
@ -117,6 +127,13 @@ public class Modules {
JLRMA.addExportsToAll(m, pn);
}
/**
* Updates a module m to open a package to all modules.
*/
public static void addOpensToAll(Module m, String pn) {
JLRMA.addOpensToAll(m, pn);
}
/**
* Updates module m to export a package to all unnamed modules.
*/
@ -124,6 +141,47 @@ public class Modules {
JLRMA.addExportsToAllUnnamed(m, pn);
}
/**
* Updates module m to open a package to all unnamed modules.
*/
public static void addOpensToAllUnnamed(Module m, String pn) {
JLRMA.addOpensToAllUnnamed(m, pn);
}
/**
* Updates module m to use a service
*/
public static void addUses(Module m, Class<?> service) {
JLRMA.addUses(m, service);
}
/**
* Updates module m to provide a service
*/
public static void addProvides(Module m, Class<?> service, Class<?> impl) {
Layer layer = m.getLayer();
if (layer == null || layer == Layer.boot()) {
// update ClassLoader catalog
PrivilegedAction<ClassLoader> pa = m::getClassLoader;
ClassLoader loader = AccessController.doPrivileged(pa);
ServicesCatalog catalog;
if (loader == null) {
catalog = BootLoader.getServicesCatalog();
} else {
catalog = ServicesCatalog.getServicesCatalog(loader);
}
catalog.addProvider(m, service, impl);
}
if (layer != null) {
// update Layer catalog
SharedSecrets.getJavaLangReflectModuleAccess()
.getServicesCatalog(layer)
.addProvider(m, service, impl);
}
}
/**
* Adds a package to a module's content.
*
@ -142,5 +200,4 @@ public class Modules {
addReads(m, BootLoader.getUnnamedModule());
addReads(m, ClassLoaders.appClassLoader().getUnnamedModule());
}
}

@ -28,20 +28,24 @@ package jdk.internal.module;
import java.lang.reflect.Module;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Provides;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import jdk.internal.loader.ClassLoaderValue;
/**
* A <em>services catalog</em>. Each {@code ClassLoader} and {@code Layer} has
* an optional {@code ServicesCatalog} for modules that provide services.
*
* @see java.util.ServiceLoader
* @apiNote This class will be replaced once the ServiceLoader is further
* specified
*/
public interface ServicesCatalog {
public final class ServicesCatalog {
/**
* Represents a service provider in the services catalog.
@ -78,56 +82,98 @@ public interface ServicesCatalog {
}
}
/**
* Registers the providers in the given module in this services catalog.
*
* @throws UnsupportedOperationException
* If this services catalog is immutable
*/
void register(Module module);
// service name -> list of providers
private final Map<String, List<ServiceProvider>> map = new ConcurrentHashMap<>();
/**
* Returns the (possibly empty) set of service providers that implement the
* given service type.
*/
Set<ServiceProvider> findServices(String service);
private ServicesCatalog() { }
/**
* Creates a ServicesCatalog that supports concurrent registration and
* and lookup.
* and lookup
*/
static ServicesCatalog create() {
return new ServicesCatalog() {
private Map<String, Set<ServiceProvider>> map = new ConcurrentHashMap<>();
@Override
public void register(Module m) {
ModuleDescriptor descriptor = m.getDescriptor();
for (Provides provides : descriptor.provides().values()) {
String service = provides.service();
Set<String> providerNames = provides.providers();
// create a new set to replace the existing
Set<ServiceProvider> result = new HashSet<>();
Set<ServiceProvider> providers = map.get(service);
if (providers != null) {
result.addAll(providers);
}
for (String pn : providerNames) {
result.add(new ServiceProvider(m, pn));
}
map.put(service, Collections.unmodifiableSet(result));
}
}
@Override
public Set<ServiceProvider> findServices(String service) {
return map.getOrDefault(service, Collections.emptySet());
}
};
public static ServicesCatalog create() {
return new ServicesCatalog();
}
/**
* Returns the list of service provides for the given service type
* name, creating it if needed.
*/
private List<ServiceProvider> providers(String service) {
// avoid computeIfAbsent here
List<ServiceProvider> list = map.get(service);
if (list == null) {
list = new CopyOnWriteArrayList<>();
List<ServiceProvider> prev = map.putIfAbsent(service, list);
if (prev != null)
list = prev; // someone else got there
}
return list;
}
/**
* Registers the providers in the given module in this services catalog.
*/
public void register(Module module) {
ModuleDescriptor descriptor = module.getDescriptor();
for (Provides provides : descriptor.provides()) {
String service = provides.service();
List<String> providerNames = provides.providers();
int count = providerNames.size();
if (count == 1) {
String pn = providerNames.get(0);
providers(service).add(new ServiceProvider(module, pn));
} else {
List<ServiceProvider> list = new ArrayList<>(count);
for (String pn : providerNames) {
list.add(new ServiceProvider(module, pn));
}
providers(service).addAll(list);
}
}
}
/**
* Add a provider in the given module to this services catalog
*
* @apiNote This method is for use by java.lang.instrument
*/
public void addProvider(Module module, Class<?> service, Class<?> impl) {
List<ServiceProvider> list = providers(service.getName());
list.add(new ServiceProvider(module, impl.getName()));
}
/**
* Returns the (possibly empty) list of service providers that implement
* the given service type.
*/
public List<ServiceProvider> findServices(String service) {
return map.getOrDefault(service, Collections.emptyList());
}
/**
* Returns the ServicesCatalog for the given class loader or {@code null}
* if there is none.
*/
public static ServicesCatalog getServicesCatalogOrNull(ClassLoader loader) {
return CLV.get(loader);
}
/**
* Returns the ServicesCatalog for the given class loader, creating it if
* needed.
*/
public static ServicesCatalog getServicesCatalog(ClassLoader loader) {
// CLV.computeIfAbsent(loader, (cl, clv) -> create());
ServicesCatalog catalog = CLV.get(loader);
if (catalog == null) {
catalog = create();
ServicesCatalog previous = CLV.putIfAbsent(loader, catalog);
if (previous != null) catalog = previous;
}
return catalog;
}
// the ServicesCatalog registered to a class loader
private static final ClassLoaderValue<ServicesCatalog> CLV = new ClassLoaderValue<>();
}

@ -51,7 +51,7 @@ public final class SystemModules {
/**
* Hash of system modules.
*/
public static String[] MODULES_TO_HASH = new String[0];
public static byte[][] MODULES_TO_HASH = new byte[0][];
/**
* Number of packages in the boot layer from the installed modules.

@ -695,7 +695,7 @@ public class ClassWriter extends ClassVisitor {
final String[] interfaces) {
this.version = version;
this.access = access;
this.name = newClass(name);
this.name = (name == null) ? 0 : newClass(name);
thisName = name;
if (ClassReader.SIGNATURES && signature != null) {
this.signature = newUTF8(signature);

@ -112,10 +112,13 @@ public class Reflection {
}
}
private static boolean verifyMemberAccess(Class<?> currentClass,
Class<?> memberClass,
Class<?> targetClass,
int modifiers)
/**
* Verify access to a member, returning {@code false} if no access
*/
public static boolean verifyMemberAccess(Class<?> currentClass,
Class<?> memberClass,
Class<?> targetClass,
int modifiers)
{
// Verify that currentClass can access a field, method, or
// constructor of memberClass, where that member's access bits are

@ -115,16 +115,16 @@ module java.base {
// additional qualified exports may be inserted at build time
// see make/gensrc/GenModuleInfo.gmk
// CORBA serialization needs reflective access
exports sun.util.calendar to
java.corba;
exports com.sun.security.ntlm to
java.security.sasl;
exports jdk.internal.jimage to
jdk.jlink;
exports jdk.internal.jimage.decompressor to
jdk.jlink;
exports jdk.internal.loader to
java.instrument,
java.logging,
jdk.jlink;
exports jdk.internal.jmod to
jdk.compiler,
jdk.jlink;
@ -146,9 +146,6 @@ module java.base {
jdk.scripting.nashorn;
exports jdk.internal.org.objectweb.asm.signature to
jdk.scripting.nashorn;
exports jdk.internal.loader to
java.instrument,
java.logging;
exports jdk.internal.math to
java.desktop;
exports jdk.internal.module to
@ -307,6 +304,7 @@ module java.base {
// JDK-internal service types
uses jdk.internal.logger.DefaultLoggerFinder;
uses sun.security.ssl.ClientKeyExchangeService;
uses sun.security.util.AuthResourcesProvider;
uses sun.util.spi.CalendarProvider;
uses sun.util.locale.provider.LocaleDataMetaInfo;
uses sun.util.resources.LocaleData.CommonResourceBundleProvider;
@ -317,4 +315,6 @@ module java.base {
provides java.nio.file.spi.FileSystemProvider with
jdk.internal.jrtfs.JrtFileSystemProvider;
provides sun.security.util.AuthResourcesProvider with
sun.security.util.AuthResourcesProviderImpl;
}

@ -48,6 +48,7 @@ import java.lang.module.ModuleReference;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.reflect.Layer;
import java.lang.reflect.Method;
@ -62,24 +63,29 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.text.Normalizer;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Locale.Category;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.misc.VM;
import jdk.internal.module.Modules;
public final class LauncherHelper {
@ -95,6 +101,8 @@ public final class LauncherHelper {
private static final String JAVAFX_FXHELPER_CLASS_NAME_SUFFIX =
"sun.launcher.LauncherHelper$FXHelper";
private static final String MAIN_CLASS = "Main-Class";
private static final String ADD_EXPORTS = "Add-Exports";
private static final String ADD_OPENS = "Add-Opens";
private static StringBuilder outBuf = new StringBuilder();
@ -413,11 +421,23 @@ public final class LauncherHelper {
if (mainAttrs == null) {
abort(null, "java.launcher.jar.error3", jarname);
}
// Main-Class
mainValue = mainAttrs.getValue(MAIN_CLASS);
if (mainValue == null) {
abort(null, "java.launcher.jar.error3", jarname);
}
// Add-Exports and Add-Opens to break encapsulation
String exports = mainAttrs.getValue(ADD_EXPORTS);
if (exports != null) {
addExportsOrOpens(exports, false);
}
String opens = mainAttrs.getValue(ADD_OPENS);
if (opens != null) {
addExportsOrOpens(opens, true);
}
/*
* Hand off to FXHelper if it detects a JavaFX application
* This must be done after ensuring a Main-Class entry
@ -436,6 +456,29 @@ public final class LauncherHelper {
return null;
}
/**
* Process the Add-Exports or Add-Opens value. The value is
* {@code <module>/<package> ( <module>/<package>)*}.
*/
static void addExportsOrOpens(String value, boolean open) {
for (String moduleAndPackage : value.split(" ")) {
String[] s = moduleAndPackage.trim().split("/");
if (s.length == 2) {
String mn = s[0];
String pn = s[1];
Layer.boot().findModule(mn).ifPresent(m -> {
if (m.getDescriptor().packages().contains(pn)) {
if (open) {
Modules.addOpensToAllUnnamed(m, pn);
} else {
Modules.addExportsToAllUnnamed(m, pn);
}
}
});
}
}
}
// From src/share/bin/java.c:
// enum LaunchMode { LM_UNKNOWN = 0, LM_CLASS, LM_JAR, LM_MODULE }
@ -899,12 +942,26 @@ public final class LauncherHelper {
for (String name: names) {
ModuleReference mref = finder.find(name).orElse(null);
if (mref == null) {
// not found
System.err.format("%s not observable!%n", name);
continue;
}
ModuleDescriptor md = mref.descriptor();
ostream.println(midAndLocation(md, mref.location()));
if (md.isOpen())
ostream.print("open ");
if (md.isAutomatic())
ostream.print("automatic ");
ostream.println("module " + midAndLocation(md, mref.location()));
// unqualified exports (sorted by package)
Set<Exports> exports = new TreeSet<>(Comparator.comparing(Exports::source));
md.exports().stream().filter(e -> !e.isQualified()).forEach(exports::add);
for (Exports e : exports) {
String modsAndSource = Stream.concat(toStringStream(e.modifiers()),
Stream.of(e.source()))
.collect(Collectors.joining(" "));
ostream.format(" exports %s%n", modsAndSource);
}
for (Requires d : md.requires()) {
ostream.format(" requires %s%n", d);
@ -913,31 +970,51 @@ public final class LauncherHelper {
ostream.format(" uses %s%n", s);
}
// sorted exports
Set<Exports> exports = new TreeSet<>(Comparator.comparing(Exports::source));
exports.addAll(md.exports());
for (Exports e : exports) {
ostream.format(" exports %s", e.source());
for (Provides ps : md.provides()) {
ostream.format(" provides %s with %s%n", ps.service(),
ps.providers().stream().collect(Collectors.joining(", ")));
}
// qualified exports
for (Exports e : md.exports()) {
if (e.isQualified()) {
String modsAndSource = Stream.concat(toStringStream(e.modifiers()),
Stream.of(e.source()))
.collect(Collectors.joining(" "));
ostream.format(" exports %s", modsAndSource);
formatCommaList(ostream, " to", e.targets());
} else {
ostream.println();
}
}
// concealed packages
new TreeSet<>(md.conceals())
.forEach(p -> ostream.format(" conceals %s%n", p));
Map<String, Provides> provides = md.provides();
for (Provides ps : provides.values()) {
for (String impl : ps.providers())
ostream.format(" provides %s with %s%n", ps.service(), impl);
// open packages
for (Opens obj: md.opens()) {
String modsAndSource = Stream.concat(toStringStream(obj.modifiers()),
Stream.of(obj.source()))
.collect(Collectors.joining(" "));
ostream.format(" opens %s", modsAndSource);
if (obj.isQualified())
formatCommaList(ostream, " to", obj.targets());
else
ostream.println();
}
// non-exported/non-open packages
Set<String> concealed = new TreeSet<>(md.packages());
md.exports().stream().map(Exports::source).forEach(concealed::remove);
md.opens().stream().map(Opens::source).forEach(concealed::remove);
concealed.forEach(p -> ostream.format(" contains %s%n", p));
}
}
}
static <T> String toString(Set<T> s) {
return toStringStream(s).collect(Collectors.joining(" "));
}
static <T> Stream<String> toStringStream(Set<T> s) {
return s.stream().map(e -> e.toString().toLowerCase());
}
static String midAndLocation(ModuleDescriptor md, Optional<URI> location ) {
URI loc = location.orElse(null);
if (loc == null || loc.getScheme().equalsIgnoreCase("jrt"))

@ -28,8 +28,8 @@ java.launcher.opt.header = Usage: {0} [options] class [args...]\n\
\ (to execute a class)\n or {0} [options] -jar jarfile [args...]\n\
\ (to execute a jar file)\n\
\ or {0} [options] -p <modulepath> -m <modulename>[/<mainclass>] [args...]\n\
\ (to execute the main class in a module)\n\
where options include:\n
\ (to execute the main class in a module)\n\n\
where options include:\n\n
java.launcher.opt.datamodel =\ -d{0}\t Deprecated, will be removed in a future release\n
java.launcher.opt.vmselect =\ {0}\t to select the "{1}" VM\n
@ -68,11 +68,16 @@ java.launcher.opt.footer =\ -cp <class search path of directories and zip
\ set a system property\n\
\ -verbose:[class|gc|jni]\n\
\ enable verbose output\n\
\ -version print product version and exit\n\
\ -showversion print product version and continue\n\
\ -? -help --help\n\
\ print this help message\n\
\ -X print help on non-standard options\n\
\ -version print product version to the error stream and exit\n\
\ --version print product version to the output stream and exit\n\
\ -showversion print product version to the error stream and continue\n\
\ --show-version\n\
\ print product version to the output stream and continue\n\
\ -? -h -help\n\
\ print this help message to the error stream\n\
\ --help print this help message to the output stream\n\
\ -X print help on extra options to the error stream\n\
\ --help-extra print help on extra options to the output stream\n\
\ -ea[:<packagename>...|:<classname>]\n\
\ -enableassertions[:<packagename>...|:<classname>]\n\
\ enable assertions with specified granularity\n\
@ -98,14 +103,12 @@ java.launcher.opt.footer =\ -cp <class search path of directories and zip
\ The most appropriate scaled image provided will be picked up\n\
\ automatically.\n\
\ See the SplashScreen API documentation for more information.\n\
\ @<filepath> read options from the specified file\n\
\ @<filepath> read options from the specified file\n\n\
\To specify an argument for a long option, you can use --<name>=<value> or\n\
\--<name> <value>.\n\
See http://www.oracle.com/technetwork/java/javase/documentation/index.html for more details.
\--<name> <value>.\n
# Translators please note do not translate the options themselves
java.launcher.X.usage=\
java.launcher.X.usage=\n\
\ -Xbatch disable background compilation\n\
\ -Xbootclasspath/a:<directories and zip/jar files separated by {0}>\n\
\ append to end of bootstrap class path\n\
@ -152,10 +155,13 @@ java.launcher.X.usage=\
\ regardless of module declaration.\n\
\ <target-module> can be ALL-UNNAMED to export to all\n\
\ unnamed modules.\n\
\ --add-opens <module>/<package>=<target-module>(,<target-module>)*\n\
\ updates <module> to open <package> to\n\
\ <target-module>, regardless of module declaration.\n\
\ --patch-module <module>=<file>({0}<file>)*\n\
\ Override or augment a module with classes and resources\n\
\ in JAR files or directories.\n\n\
These options are non-standard and subject to change without notice.\n
These extra options are subject to change without notice.\n
# Translators please note do not translate the options themselves
java.launcher.X.macosx.usage=\

@ -0,0 +1,29 @@
/*
* Copyright (c) 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.security.util;
public interface AuthResourcesProvider extends java.util.spi.ResourceBundleProvider {
}

@ -0,0 +1,35 @@
/*
* Copyright (c) 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.security.util;
import java.util.spi.AbstractResourceBundleProvider;
public final class AuthResourcesProviderImpl extends AbstractResourceBundleProvider
implements AuthResourcesProvider {
public AuthResourcesProviderImpl() {
super("java.class");
}
}

@ -1,149 +0,0 @@
/*
* Copyright (c) 2015, 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Module;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
/**
* ResourceBundleProviderSupport provides convenience methods for loading
* resource bundles.
*/
public class ResourceBundleProviderSupport {
/**
* Loads a {@code ResourceBundle} of the given {@code bundleName} local to
* the given {@code module}.
*
* @apiNote
* {@link Class#forName(Module, String)} does a stack-based permission check.
* Caller of this method is responsible for doing an appropriate permission
* on behalf of the caller before calling this method.
*
* @param module the module from which the {@code ResourceBundle} is loaded
* @param bundleName the bundle name for the {@code ResourceBundle} class,
* such as "com.example.app.MyResources_fr"
* @return the {@code ResourceBundle}, or null if no {@code ResourceBundle} is found
* @throws SecurityException
* if a security manager exists, it denies loading the class given by
* {@code bundleName} from the given {@code module}.
* If the given module is "java.base", this method will not do security check.
* @throws NullPointerException
* if {@code module} or {@code bundleName) is null
* @see Class#forName(Module, String)
*/
public static ResourceBundle loadResourceBundle(Module module, String bundleName)
{
Class<?> c = Class.forName(module, bundleName);
if (c != null && ResourceBundle.class.isAssignableFrom(c)) {
try {
@SuppressWarnings("unchecked")
Class<ResourceBundle> bundleClass = (Class<ResourceBundle>) c;
Constructor<ResourceBundle> ctor = bundleClass.getConstructor();
if (!Modifier.isPublic(ctor.getModifiers())) {
return null;
}
// java.base may not be able to read the bundleClass's module.
PrivilegedAction<Void> pa1 = () -> { ctor.setAccessible(true); return null; };
AccessController.doPrivileged(pa1);
try {
return ctor.newInstance((Object[]) null);
} catch (InvocationTargetException e) {
uncheckedThrow(e);
} catch (InstantiationException | IllegalAccessException e) {
throw new InternalError(e);
}
} catch (NoSuchMethodException e) {
}
}
return null;
}
@SuppressWarnings("unchecked")
private static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
if (t != null)
throw (T)t;
else
throw new Error("Unknown Exception");
}
/**
* Loads properties of the given {@code bundleName} local to the given
* {@code module} and returns a {@code ResourceBundle} produced from the
* loaded properties.
*
* @apiNote This method is intended for internal use. Need to refactor.
*
* @param module the module from which the properties are loaded
* @param bundleName the bundle name of the properties,
* such as "com.example.app.MyResources_de"
* @return the {@code ResourceBundle} produced from the loaded properties,
* or null if no properties are found
* @see PropertyResourceBundle
*/
public static ResourceBundle loadPropertyResourceBundle(Module module, String bundleName)
throws IOException
{
String resourceName = toResourceName(bundleName, "properties");
if (resourceName == null) {
return null;
}
PrivilegedAction<InputStream> pa = () -> {
try {
return module.getResourceAsStream(resourceName);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
try (InputStream stream = AccessController.doPrivileged(pa)) {
if (stream != null) {
return new PropertyResourceBundle(stream);
} else {
return null;
}
} catch (UncheckedIOException e) {
throw e.getCause();
}
}
private static String toResourceName(String bundleName, String suffix) {
if (bundleName.contains("://")) {
return null;
}
StringBuilder sb = new StringBuilder(bundleName.length() + 1 + suffix.length());
sb.append(bundleName.replace('.', '/')).append('.').append(suffix);
return sb.toString();
}
}

@ -58,8 +58,6 @@ import java.util.concurrent.ConcurrentMap;
import java.util.spi.ResourceBundleProvider;
import jdk.internal.misc.JavaUtilResourceBundleAccess;
import jdk.internal.misc.SharedSecrets;
import sun.util.locale.provider.ResourceBundleProviderSupport;
/**
*/
@ -203,9 +201,13 @@ public abstract class Bundles {
bundle = loadBundleFromProviders(baseName, targetLocale, providers, cacheKey);
} else {
try {
bundle = ResourceBundleProviderSupport
.loadResourceBundle(Bundles.class.getModule(),
strategy.toBundleName(baseName, targetLocale));
String bundleName = strategy.toBundleName(baseName, targetLocale);
Class<?> c = Class.forName(Bundles.class.getModule(), bundleName);
if (c != null && ResourceBundle.class.isAssignableFrom(c)) {
@SuppressWarnings("unchecked")
Class<ResourceBundle> bundleClass = (Class<ResourceBundle>) c;
bundle = bundleAccess.newResourceBundle(bundleClass);
}
} catch (Exception e) {
cacheKey.setCause(e);
}

@ -770,12 +770,6 @@ struct JNINativeInterface_ {
jobject (JNICALL *GetModule)
(JNIEnv* env, jclass clazz);
void (JNICALL *AddModuleReads)
(JNIEnv* env, jobject m1, jobject m2);
jboolean (JNICALL *CanReadModule)
(JNIEnv* env, jobject m1, jobject m2);
};
/*
@ -1874,14 +1868,6 @@ struct JNIEnv_ {
return functions->GetModule(this, clazz);
}
void AddModuleReads(jobject m1, jobject m2) {
functions->AddModuleReads(this, m1, m2);
}
jboolean CanReadModule(jobject m1, jobject m2) {
return functions->CanReadModule(this, m1, m2);
}
#endif /* __cplusplus */
};

@ -402,8 +402,8 @@ JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader,
*/
JNIEXPORT void JNICALL
JVM_DefineModule(JNIEnv *env, jobject module, jstring version, jstring location,
jobjectArray packages);
JVM_DefineModule(JNIEnv *env, jobject module, jboolean is_open, jstring version,
jstring location, jobjectArray packages);
JNIEXPORT void JNICALL
JVM_SetBootLoaderUnnamedModule(JNIEnv *env, jobject module);
@ -411,9 +411,6 @@ JVM_SetBootLoaderUnnamedModule(JNIEnv *env, jobject module);
JNIEXPORT void JNICALL
JVM_AddReadsModule(JNIEnv *env, jobject from_module, jobject to_module);
JNIEXPORT jboolean JNICALL
JVM_CanReadModule(JNIEnv *env, jobject asking_module, jobject source_module);
JNIEXPORT void JNICALL
JVM_AddModuleExports(JNIEnv *env, jobject from_module, jstring package, jobject to_module);
@ -423,9 +420,6 @@ JVM_AddModuleExportsToAll(JNIEnv *env, jobject from_module, jstring package);
JNIEXPORT void JNICALL
JVM_AddModuleExportsToAllUnnamed(JNIEnv *env, jobject from_module, jstring package);
JNIEXPORT jboolean JNICALL
JVM_IsExportedToModule(JNIEnv *env, jobject from_module, jstring package, jobject to_module);
JNIEXPORT void JNICALL
JVM_AddModulePackage(JNIEnv* env, jobject module, jstring package);

@ -30,10 +30,10 @@
JNIEXPORT void JNICALL
Java_java_lang_reflect_Module_defineModule0(JNIEnv *env, jclass cls, jobject module,
jstring version, jstring location,
jobjectArray packages)
jboolean is_open, jstring version,
jstring location, jobjectArray packages)
{
JVM_DefineModule(env, module, version, location, packages);
JVM_DefineModule(env, module, is_open, version, location, packages);
}
JNIEXPORT void JNICALL

@ -61,12 +61,13 @@
* interfaces.
*/
/* we always print to stderr */
#define USE_STDERR JNI_TRUE
#define USE_STDERR JNI_TRUE /* we usually print to stderr */
#define USE_STDOUT JNI_FALSE
static jboolean printVersion = JNI_FALSE; /* print and exit */
static jboolean showVersion = JNI_FALSE; /* print but continue */
static jboolean printUsage = JNI_FALSE; /* print and exit*/
static jboolean printTo = USE_STDERR; /* where to print version/usage */
static jboolean printXUsage = JNI_FALSE; /* print and exit*/
static jboolean dryRun = JNI_FALSE; /* initialize VM and exit */
static char *showSettings = NULL; /* print but continue */
@ -567,6 +568,7 @@ IsModuleOption(const char* name) {
JLI_StrCmp(name, "--add-modules") == 0 ||
JLI_StrCmp(name, "--limit-modules") == 0 ||
JLI_StrCmp(name, "--add-exports") == 0 ||
JLI_StrCmp(name, "--add-opens") == 0 ||
JLI_StrCmp(name, "--add-reads") == 0 ||
JLI_StrCmp(name, "--patch-module") == 0;
}
@ -758,12 +760,15 @@ SetJvmEnvironment(int argc, char **argv) {
if (*arg != '-'
|| JLI_StrCmp(arg, "-version") == 0
|| JLI_StrCmp(arg, "--version") == 0
|| JLI_StrCmp(arg, "-fullversion") == 0
|| JLI_StrCmp(arg, "--full-version") == 0
|| JLI_StrCmp(arg, "-help") == 0
|| JLI_StrCmp(arg, "--help") == 0
|| JLI_StrCmp(arg, "-?") == 0
|| JLI_StrCmp(arg, "-jar") == 0
|| JLI_StrCmp(arg, "-X") == 0) {
|| JLI_StrCmp(arg, "-X") == 0
|| JLI_StrCmp(arg, "--help-extra") == 0) {
return;
}
}
@ -1288,6 +1293,7 @@ ParseArguments(int *pargc, char ***pargv,
} else if (JLI_StrCmp(arg, "--add-modules") == 0 ||
JLI_StrCmp(arg, "--limit-modules") == 0 ||
JLI_StrCmp(arg, "--add-exports") == 0 ||
JLI_StrCmp(arg, "--add-opens") == 0 ||
JLI_StrCmp(arg, "--add-reads") == 0 ||
JLI_StrCmp(arg, "--patch-module") == 0) {
REPORT_ERROR (has_arg, ARG_ERROR6, arg);
@ -1295,22 +1301,36 @@ ParseArguments(int *pargc, char ***pargv,
/*
* The following cases will cause the argument parsing to stop
*/
} else if (JLI_StrCmp(arg, "--help") == 0 ||
JLI_StrCmp(arg, "-help") == 0 ||
} else if (JLI_StrCmp(arg, "-help") == 0 ||
JLI_StrCmp(arg, "-h") == 0 ||
JLI_StrCmp(arg, "-?") == 0) {
printUsage = JNI_TRUE;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "--help") == 0) {
printUsage = JNI_TRUE;
printTo = USE_STDOUT;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "-version") == 0) {
printVersion = JNI_TRUE;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "--version") == 0) {
printVersion = JNI_TRUE;
printTo = USE_STDOUT;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "-showversion") == 0) {
showVersion = JNI_TRUE;
} else if (JLI_StrCmp(arg, "--show-version") == 0) {
showVersion = JNI_TRUE;
printTo = USE_STDOUT;
} else if (JLI_StrCmp(arg, "--dry-run") == 0) {
dryRun = JNI_TRUE;
} else if (JLI_StrCmp(arg, "-X") == 0) {
printXUsage = JNI_TRUE;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "--help-extra") == 0) {
printXUsage = JNI_TRUE;
printTo = USE_STDOUT;
return JNI_TRUE;
/*
* The following case checks for -XshowSettings OR -XshowSetting:SUBOPT.
* In the latter case, any SUBOPT value not recognized will default to "all"
@ -1330,6 +1350,9 @@ ParseArguments(int *pargc, char ***pargv,
} else if (JLI_StrCmp(arg, "-fullversion") == 0) {
JLI_ReportMessage("%s full version \"%s\"", _launcher_name, GetFullVersion());
return JNI_FALSE;
} else if (JLI_StrCmp(arg, "--full-version") == 0) {
JLI_ShowMessage("%s %s", _launcher_name, GetFullVersion());
return JNI_FALSE;
} else if (JLI_StrCmp(arg, "-verbosegc") == 0) {
AddOption("-verbose:gc", NULL);
} else if (JLI_StrCmp(arg, "-t") == 0) {
@ -1752,11 +1775,11 @@ PrintJavaVersion(JNIEnv *env, jboolean extraLF)
NULL_CHECK(print = (*env)->GetStaticMethodID(env,
ver,
(extraLF == JNI_TRUE) ? "println" : "print",
"()V"
"(Z)V"
)
);
(*env)->CallStaticVoidMethod(env, ver, print);
(*env)->CallStaticVoidMethod(env, ver, print, printTo);
}
/*
@ -1794,7 +1817,7 @@ ListModules(JNIEnv *env, char *optString)
"listModules", "(ZLjava/lang/String;)V"));
NULL_CHECK(joptString = (*env)->NewStringUTF(env, optString));
(*env)->CallStaticVoidMethod(env, cls, listModulesID,
USE_STDERR,
USE_STDOUT,
joptString);
}
@ -1812,7 +1835,7 @@ PrintUsage(JNIEnv* env, jboolean doXUsage)
if (doXUsage) {
NULL_CHECK(printXUsageMessage = (*env)->GetStaticMethodID(env, cls,
"printXUsageMessage", "(Z)V"));
(*env)->CallStaticVoidMethod(env, cls, printXUsageMessage, USE_STDERR);
(*env)->CallStaticVoidMethod(env, cls, printXUsageMessage, printTo);
} else {
NULL_CHECK(initHelp = (*env)->GetStaticMethodID(env, cls,
"initHelpMessage", "(Ljava/lang/String;)V"));
@ -1853,7 +1876,7 @@ PrintUsage(JNIEnv* env, jboolean doXUsage)
}
/* Complete the usage message and print to stderr*/
(*env)->CallStaticVoidMethod(env, cls, printHelp, USE_STDERR);
(*env)->CallStaticVoidMethod(env, cls, printHelp, printTo);
}
return;
}
@ -2255,3 +2278,16 @@ JLI_ReportMessage(const char* fmt, ...)
fprintf(stderr, "\n");
va_end(vl);
}
/*
* A utility procedure to always print to stdout
*/
void
JLI_ShowMessage(const char* fmt, ...)
{
va_list vl;
va_start(vl, fmt);
vfprintf(stdout, fmt, vl);
fprintf(stdout, "\n");
va_end(vl);
}

@ -140,6 +140,9 @@ void JLI_ReportErrorMessageSys(const char * message, ...);
/* Reports an error message only to stderr. */
void JLI_ReportMessage(const char * message, ...);
/* Reports a message only to stdout. */
void JLI_ShowMessage(const char * message, ...);
/*
* Reports an exception which terminates the vm to stderr or a window
* as appropriate.

@ -27,7 +27,7 @@
* Aggregates {@code java.base}, {@code java.logging}, and {@code java.scripting}.
*/
module java.compact1 {
requires public java.logging;
requires public java.scripting;
requires transitive java.logging;
requires transitive java.scripting;
}

@ -27,9 +27,9 @@
* Supplements {@code java.compact1} with JDBC, JAXP, and RMI.
*/
module java.compact2 {
requires public java.compact1;
requires public java.rmi;
requires public java.sql;
requires public java.xml;
requires transitive java.compact1;
requires transitive java.rmi;
requires transitive java.sql;
requires transitive java.xml;
}

@ -28,15 +28,15 @@
* Instrumentation, Preferences, Security, and XML cryptography APIs.
*/
module java.compact3 {
requires public java.compact2;
requires public java.compiler;
requires public java.instrument;
requires public java.management;
requires public java.naming;
requires public java.prefs;
requires public java.security.jgss;
requires public java.security.sasl;
requires public java.sql.rowset;
requires public java.xml.crypto;
requires transitive java.compact2;
requires transitive java.compiler;
requires transitive java.instrument;
requires transitive java.management;
requires transitive java.naming;
requires transitive java.prefs;
requires transitive java.security.jgss;
requires transitive java.security.sasl;
requires transitive java.sql.rowset;
requires transitive java.xml.crypto;
}

@ -28,8 +28,8 @@
* accessibility, audio, imaging, printing, and JavaBeans.
*/
module java.desktop {
requires public java.datatransfer;
requires public java.xml;
requires transitive java.datatransfer;
requires transitive java.xml;
requires java.prefs;
exports java.applet;
@ -91,6 +91,11 @@ module java.desktop {
exports com.sun.awt to
jdk.desktop;
opens javax.swing.plaf.basic to
jdk.jconsole;
opens com.sun.java.swing.plaf.windows to
jdk.jconsole;
uses java.awt.im.spi.InputMethodDescriptor;
uses javax.accessibility.AccessibilityProvider;
uses javax.imageio.spi.ImageInputStreamSpi;
@ -113,31 +118,44 @@ module java.desktop {
provides java.net.ContentHandlerFactory with sun.awt.www.content.MultimediaContentHandlers;
provides javax.print.PrintServiceLookup with sun.print.PrintServiceLookupProvider;
provides javax.print.StreamPrintServiceFactory with sun.print.PSStreamPrinterFactory;
provides javax.sound.midi.spi.MidiDeviceProvider with com.sun.media.sound.MidiInDeviceProvider;
provides javax.sound.midi.spi.MidiDeviceProvider with com.sun.media.sound.MidiOutDeviceProvider;
provides javax.sound.midi.spi.MidiDeviceProvider with com.sun.media.sound.RealTimeSequencerProvider;
provides javax.sound.midi.spi.MidiDeviceProvider with com.sun.media.sound.SoftProvider;
provides javax.sound.midi.spi.MidiDeviceProvider with
com.sun.media.sound.MidiInDeviceProvider,
com.sun.media.sound.MidiOutDeviceProvider,
com.sun.media.sound.RealTimeSequencerProvider,
com.sun.media.sound.SoftProvider;
provides javax.sound.midi.spi.MidiFileReader with com.sun.media.sound.StandardMidiFileReader;
provides javax.sound.midi.spi.MidiFileWriter with com.sun.media.sound.StandardMidiFileWriter;
provides javax.sound.midi.spi.SoundbankReader with com.sun.media.sound.AudioFileSoundbankReader;
provides javax.sound.midi.spi.SoundbankReader with com.sun.media.sound.DLSSoundbankReader;
provides javax.sound.midi.spi.SoundbankReader with com.sun.media.sound.JARSoundbankReader;
provides javax.sound.midi.spi.SoundbankReader with com.sun.media.sound.SF2SoundbankReader;
provides javax.sound.sampled.spi.AudioFileReader with com.sun.media.sound.AiffFileReader;
provides javax.sound.sampled.spi.AudioFileReader with com.sun.media.sound.AuFileReader;
provides javax.sound.sampled.spi.AudioFileReader with com.sun.media.sound.SoftMidiAudioFileReader;
provides javax.sound.sampled.spi.AudioFileReader with com.sun.media.sound.WaveFileReader;
provides javax.sound.sampled.spi.AudioFileReader with com.sun.media.sound.WaveFloatFileReader;
provides javax.sound.sampled.spi.AudioFileReader with com.sun.media.sound.WaveExtensibleFileReader;
provides javax.sound.sampled.spi.AudioFileWriter with com.sun.media.sound.AiffFileWriter;
provides javax.sound.sampled.spi.AudioFileWriter with com.sun.media.sound.AuFileWriter;
provides javax.sound.sampled.spi.AudioFileWriter with com.sun.media.sound.WaveFileWriter;
provides javax.sound.sampled.spi.AudioFileWriter with com.sun.media.sound.WaveFloatFileWriter;
provides javax.sound.sampled.spi.FormatConversionProvider with com.sun.media.sound.AlawCodec;
provides javax.sound.sampled.spi.FormatConversionProvider with com.sun.media.sound.AudioFloatFormatConverter;
provides javax.sound.sampled.spi.FormatConversionProvider with com.sun.media.sound.PCMtoPCMCodec;
provides javax.sound.sampled.spi.FormatConversionProvider with com.sun.media.sound.UlawCodec;
provides javax.sound.sampled.spi.MixerProvider with com.sun.media.sound.DirectAudioDeviceProvider;
provides javax.sound.sampled.spi.MixerProvider with com.sun.media.sound.PortMixerProvider;
provides javax.sound.midi.spi.SoundbankReader with
com.sun.media.sound.AudioFileSoundbankReader,
com.sun.media.sound.DLSSoundbankReader,
com.sun.media.sound.JARSoundbankReader,
com.sun.media.sound.SF2SoundbankReader;
provides javax.sound.sampled.spi.AudioFileReader with
com.sun.media.sound.AiffFileReader,
com.sun.media.sound.AuFileReader,
com.sun.media.sound.SoftMidiAudioFileReader,
com.sun.media.sound.WaveFileReader,
com.sun.media.sound.WaveFloatFileReader,
com.sun.media.sound.WaveExtensibleFileReader;
provides javax.sound.sampled.spi.AudioFileWriter with
com.sun.media.sound.AiffFileWriter,
com.sun.media.sound.AuFileWriter,
com.sun.media.sound.WaveFileWriter,
com.sun.media.sound.WaveFloatFileWriter;
provides javax.sound.sampled.spi.FormatConversionProvider with
com.sun.media.sound.AlawCodec,
com.sun.media.sound.AudioFloatFormatConverter,
com.sun.media.sound.PCMtoPCMCodec,
com.sun.media.sound.UlawCodec;
provides javax.sound.sampled.spi.MixerProvider with
com.sun.media.sound.DirectAudioDeviceProvider,
com.sun.media.sound.PortMixerProvider;
}

@ -362,6 +362,14 @@ public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
});
}
/**
* If the module image layout changes the location of JDK fonts,
* this will be updated to reflect that.
*/
public static final String getJDKFontDir() {
return jreFontDirName;
}
public TrueTypeFont getEUDCFont() {
// Overridden in Windows.
return null;

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -23,23 +23,17 @@
* questions.
*/
package java.lang.module;
package sun.font.lookup;
import java.util.*;
import java.util.stream.*;
import sun.font.SunFontManager;
/**
* Implementation-class accessed by other JDK modules to
* locate the JDK-provided fonts.
*/
public final class JDKFontLookup {
class Dependence {
private Dependence() { }
static <T> Stream<String> toStringStream(Set<T> s) {
return s.stream().map(e -> e.toString().toLowerCase());
public final static String getJDKFontDir() {
return SunFontManager.getJDKFontDir();
}
static <M> String toString(Set<M> mods, String what) {
return (Stream.concat(toStringStream(mods), Stream.of(what)))
.collect(Collectors.joining(" "));
}
}

@ -27,6 +27,9 @@ package java.lang.instrument;
import java.lang.reflect.Module;
import java.security.ProtectionDomain;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;
/*
@ -660,19 +663,62 @@ public interface Instrumentation {
setNativeMethodPrefix(ClassFileTransformer transformer, String prefix);
/**
* Updates a module to read another module.
* Redefine a module to expand the set of modules that it reads, the set of
* packages that it exports or opens, or the services that it uses or
* provides. This method facilitates the instrumentation of code in named
* modules where that instrumentation requires changes to the set of modules
* that are read, the packages that are exported or open, or the services
* that are used or provided.
*
* Agents that instrument code in named modules may need to arrange for the
* modules to read other modules. This method is equivalent to code in {@code
* module} calling {@link Module#addReads(Module) addReads} to read {@code
* other}.
* <p> This method cannot reduce the set of modules that a module reads, nor
* reduce the set of packages that it exports or opens, nor reduce the set
* of services that it uses or provides. This method is a no-op when invoked
* to redefine an unnamed module. </p>
*
* @param module the module to update
* @param other the module to read
* @throws NullPointerException if either module is {@code null}
* <p> When expanding the services that a module uses or provides then the
* onus is on the agent to ensure that the service type will be accessible at
* each instrumentation site where the service type is used. This method
* does not check if the service type is a member of the module or in a
* package exported to the module by another module that it reads. </p>
*
* <p> The {@code extraExports} parameter is the map of additional packages
* to export. The {@code extraOpens} parameter is the map of additional
* packages to open. In both cases, the map key is the fully-qualified name
* of the package as defined in section 6.5.3 of
* <cite>The Java&trade; Language Specification </cite>, for example, {@code
* "java.lang"}. The map value is the non-empty set of modules that the
* package should be exported or opened to. </p>
*
* <p> The {@code extraProvides} parameter is the additional service providers
* for the module to provide. The map key is the service type. The map value
* is the non-empty list of implementation types, each of which is a member
* of the module and an implementation of the service. </p>
*
* <p> This method is safe for concurrent use and so allows multiple agents
* to instrument and update the same module at around the same time. </p>
*
* @param module the module to redefine
* @param extraReads the possibly-empty set of additional modules to read
* @param extraExports the possibly-empty map of additional packages to export
* @param extraOpens the possibly-empty map of additional packages to open
* @param extraUses the possibly-empty set of additional services to use
* @param extraProvides the possibly-empty map of additional services to provide
*
* @throws IllegalArgumentException
* If {@code extraExports} or {@code extraOpens} contains a key
* that is not a package in the module; if {@code extraExports} or
* {@code extraOpens} maps a key to an empty set; if a value in the
* {@code extraProvides} map contains a service provider type that
* is not a member of the module or an implementation of the service;
* or {@code extraProvides} maps a key to an empty list
* @throws NullPointerException if any of the arguments are {@code null} or
* any of the Sets or Maps contains a {@code null} key or value
* @since 9
* @see Module#canRead(Module)
*/
void addModuleReads(Module module, Module other);
void redefineModule(Module module,
Set<Module> extraReads,
Map<String, Set<Module>> extraExports,
Map<String, Set<Module>> extraOpens,
Set<Class<?>> extraUses,
Map<Class<?>, List<Class<?>>> extraProvides);
}

@ -277,15 +277,6 @@ the <code>Agent-Class</code> attribute specifies the name of the agent class
<h3>Instrumenting code in modules</h3>
Agents that instrument code in named modules may need to arrange for the
modules to read other modules. If code is instrumented to invoke a method
in a support class in another module, then the module of the instrumented
code should read the module of the supporting class. Furthermore, the
supporting class will only be accessible to the instrumented code if
it is <code>public</code> and in a package that is exported by its module.
Agents can use {@link Instrumentation#addModuleReads addModuleReads} to update
a module to read another.
<p>
As an aid to agents that deploy supporting classes on the search path of the
bootstrap class loader, or the search path of the class loader that loads
the main agent class, the Java virtual machine arranges for the module of

@ -29,18 +29,23 @@ package sun.instrument;
import java.lang.reflect.Method;
import java.lang.reflect.Module;
import java.lang.reflect.AccessibleObject;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.Objects;
import java.util.Collections;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;
import jdk.internal.module.Modules;
/*
* Copyright 2003 Wily Technology, Inc.
*/
@ -228,10 +233,100 @@ public class InstrumentationImpl implements Instrumentation {
}
@Override
public void addModuleReads(Module module, Module other) {
Objects.requireNonNull(module);
Objects.requireNonNull(other);
jdk.internal.module.Modules.addReads(module, other);
public void redefineModule(Module module,
Set<Module> extraReads,
Map<String, Set<Module>> extraExports,
Map<String, Set<Module>> extraOpens,
Set<Class<?>> extraUses,
Map<Class<?>, List<Class<?>>> extraProvides)
{
if (!module.isNamed())
return;
// copy and check reads
extraReads = new HashSet<>(extraReads);
if (extraReads.contains(null))
throw new NullPointerException("'extraReads' contains null");
// copy and check exports and opens
extraExports = cloneAndCheckMap(module, extraExports);
extraOpens = cloneAndCheckMap(module, extraOpens);
// copy and check uses
extraUses = new HashSet<>(extraUses);
if (extraUses.contains(null))
throw new NullPointerException("'extraUses' contains null");
// copy and check provides
Map<Class<?>, List<Class<?>>> tmpProvides = new HashMap<>();
for (Map.Entry<Class<?>, List<Class<?>>> e : extraProvides.entrySet()) {
Class<?> service = e.getKey();
if (service == null)
throw new NullPointerException("'extraProvides' contains null");
List<Class<?>> providers = new ArrayList<>(e.getValue());
if (providers.isEmpty())
throw new IllegalArgumentException("list of providers is empty");
providers.forEach(p -> {
if (p.getModule() != module)
throw new IllegalArgumentException(p + " not in " + module);
if (!service.isAssignableFrom(p))
throw new IllegalArgumentException(p + " is not a " + service);
});
tmpProvides.put(service, providers);
}
extraProvides = tmpProvides;
// update reads
extraReads.forEach(m -> Modules.addReads(module, m));
// update exports
for (Map.Entry<String, Set<Module>> e : extraExports.entrySet()) {
String pkg = e.getKey();
Set<Module> targets = e.getValue();
targets.forEach(m -> Modules.addExports(module, pkg, m));
}
// update opens
for (Map.Entry<String, Set<Module>> e : extraOpens.entrySet()) {
String pkg = e.getKey();
Set<Module> targets = e.getValue();
targets.forEach(m -> Modules.addOpens(module, pkg, m));
}
// update uses
extraUses.forEach(service -> Modules.addUses(module, service));
// update provides
for (Map.Entry<Class<?>, List<Class<?>>> e : extraProvides.entrySet()) {
Class<?> service = e.getKey();
List<Class<?>> providers = e.getValue();
providers.forEach(p -> Modules.addProvides(module, service, p));
}
}
private Map<String, Set<Module>>
cloneAndCheckMap(Module module, Map<String, Set<Module>> map)
{
if (map.isEmpty())
return Collections.emptyMap();
Map<String, Set<Module>> result = new HashMap<>();
Set<String> packages = Set.of(module.getPackages());
for (Map.Entry<String, Set<Module>> e : map.entrySet()) {
String pkg = e.getKey();
if (pkg == null)
throw new NullPointerException("package cannot be null");
if (!packages.contains(pkg))
throw new IllegalArgumentException(pkg + " not in module");
Set<Module> targets = new HashSet<>(e.getValue());
if (targets.isEmpty())
throw new IllegalArgumentException("set of targets is empty");
if (targets.contains(null))
throw new NullPointerException("set of targets cannot include null");
result.put(pkg, targets);
}
return result;
}

@ -40,6 +40,8 @@ import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.Function;
import jdk.internal.loader.ClassLoaderValue;
import jdk.internal.misc.JavaUtilResourceBundleAccess;
import jdk.internal.misc.SharedSecrets;
/**
* The Level class defines a set of standard logging levels that
@ -74,7 +76,11 @@ import jdk.internal.loader.ClassLoaderValue;
*/
public class Level implements java.io.Serializable {
private static final String defaultBundle = "sun.util.logging.resources.logging";
private static final String defaultBundle =
"sun.util.logging.resources.logging";
private static final JavaUtilResourceBundleAccess RB_ACCESS =
SharedSecrets.getJavaUtilResourceBundleAccess();
/**
* @serial The non-localized name of the level.
@ -280,7 +286,7 @@ public class Level implements java.io.Serializable {
// or its defining class loader, if it's unnamed module,
// of this Level instance that can be a custom Level subclass;
Module module = this.getClass().getModule();
ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName,
ResourceBundle rb = RB_ACCESS.getBundle(resourceBundleName,
newLocale, module);
final String localizedName = rb.getString(name);

@ -38,6 +38,9 @@ import java.util.Objects;
import java.util.ResourceBundle;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Supplier;
import jdk.internal.misc.JavaUtilResourceBundleAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import static jdk.internal.logger.DefaultLoggerFinder.isSystem;
@ -256,8 +259,8 @@ public class Logger {
private static final LoggerBundle NO_RESOURCE_BUNDLE =
new LoggerBundle(null, null);
private static final RuntimePermission GET_CLASS_LOADER_PERMISSION =
new RuntimePermission("getClassLoader");
private static final JavaUtilResourceBundleAccess RB_ACCESS =
SharedSecrets.getJavaUtilResourceBundleAccess();
// A value class that holds the logger configuration data.
// This configuration can be shared between an application logger
@ -2180,9 +2183,7 @@ public class Logger {
if (!useCallersModule || callerModule == null || !callerModule.isNamed()) {
try {
Module mod = cl.getUnnamedModule();
PrivilegedAction<ResourceBundle> pa = () ->
ResourceBundle.getBundle(name, currentLocale, mod);
catalog = AccessController.doPrivileged(pa, null, GET_CLASS_LOADER_PERMISSION);
catalog = RB_ACCESS.getBundle(name, currentLocale, mod);
catalogName = name;
catalogLocale = currentLocale;
return catalog;
@ -2226,9 +2227,7 @@ public class Logger {
// Try with the caller's module
try {
// Use the caller's module
PrivilegedAction<ResourceBundle> pa = () ->
ResourceBundle.getBundle(name, currentLocale, callerModule);
catalog = AccessController.doPrivileged(pa, null, GET_CLASS_LOADER_PERMISSION);
catalog = RB_ACCESS.getBundle(name, currentLocale, callerModule);
catalogName = name;
catalogLocale = currentLocale;
return catalog;

@ -30,7 +30,7 @@
* JVM and other components in the Java runtime.
*/
module java.management {
requires public java.rmi;
requires transitive java.rmi;
requires java.logging;
requires java.naming;

@ -625,26 +625,6 @@ public abstract class MappedMXBeanType {
// that has no from method to be embeded in another class.
}
// check if a static "toCompositeData" method exists
try {
toMethod = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
public Method run() throws NoSuchMethodException {
Method m = javaClass.getDeclaredMethod("toCompositeData", javaClass);
if (m != null
&& CompositeData.class.isAssignableFrom(m.getReturnType())
&& Modifier.isStatic(m.getModifiers())) {
m.setAccessible(true);
return m;
} else {
return null;
}
}
});
} catch (PrivilegedActionException e) {
// ignore NoSuchMethodException since we allow classes
// that has no from method to be embeded in another class.
}
if (COMPOSITE_DATA_CLASS.isAssignableFrom(c)) {
// c implements CompositeData - set openType to null
// defer generating the CompositeType

@ -29,16 +29,17 @@
* This module requires {@code java.se} and supplements it with modules
* that define CORBA and Java EE APIs. These modules are upgradeable.
*/
@SuppressWarnings("deprecation")
module java.se.ee {
requires public java.se;
requires transitive java.se;
// Upgradeable modules for Java EE technologies
requires public java.activation;
requires public java.annotations.common;
requires public java.corba;
requires public java.transaction;
requires public java.xml.bind;
requires public java.xml.ws;
requires transitive java.activation;
requires transitive java.annotations.common;
requires transitive java.corba;
requires transitive java.transaction;
requires transitive java.xml.bind;
requires transitive java.xml.ws;
}

@ -31,8 +31,8 @@
* required by {@code java.se.ee}.
*/
module java.se {
requires public java.compact3;
requires public java.datatransfer;
requires public java.desktop;
requires public java.httpclient;
requires transitive java.compact3;
requires transitive java.datatransfer;
requires transitive java.desktop;
requires transitive java.httpclient;
}

@ -27,9 +27,9 @@
* Defines the JDBC RowSet API.
*/
module java.sql.rowset {
requires public java.logging;
requires public java.naming;
requires public java.sql;
requires transitive java.logging;
requires transitive java.naming;
requires transitive java.sql;
exports javax.sql.rowset;
exports javax.sql.rowset.serial;

@ -27,8 +27,8 @@
* Defines the JDBC API.
*/
module java.sql {
requires public java.logging;
requires public java.xml;
requires transitive java.logging;
requires transitive java.xml;
exports java.sql;
exports javax.sql;

@ -30,7 +30,7 @@
* exceptions by the 'Java Language to IDL Mapping Specification'.
*/
module java.transaction {
requires public java.rmi;
requires transitive java.rmi;
exports javax.transaction;
}

@ -27,7 +27,7 @@
* Defines an API for XML cryptography.
*/
module java.xml.crypto {
requires public java.xml;
requires transitive java.xml;
requires java.logging;
exports javax.xml.crypto;

@ -24,7 +24,7 @@
*/
module jdk.accessibility {
requires public java.desktop;
requires transitive java.desktop;
exports com.sun.java.accessibility.util;
}

@ -23,6 +23,9 @@
* questions.
*/
/**
* Defines the attach API.
*/
module jdk.attach {
requires jdk.jvmstat;

@ -28,7 +28,7 @@
*/
module jdk.desktop {
requires public java.desktop;
requires transitive java.desktop;
exports jdk.awt;
}

@ -23,6 +23,9 @@
* questions.
*/
/**
* Internal API for line editing
*/
module jdk.internal.le {
exports jdk.internal.jline to
jdk.scripting.nashorn.shell,

@ -23,6 +23,9 @@
* questions.
*/
/**
* Internal option processing API
*/
module jdk.internal.opt {
exports jdk.internal.joptsimple to jdk.jlink, jdk.jshell;
}

@ -30,6 +30,7 @@ import java.lang.module.Configuration;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Version;
import java.lang.module.ModuleFinder;
@ -58,6 +59,7 @@ import java.text.MessageFormat;
import jdk.internal.misc.JavaLangModuleAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.Checks;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleInfoExtender;
import jdk.internal.util.jar.JarIndex;
@ -80,7 +82,7 @@ class Main {
String fname, mname, ename;
String zname = "";
String rootjar = null;
Set<String> concealedPackages = new HashSet<>();
Set<String> concealedPackages = new HashSet<>(); // used by Validator
private static final int BASE_VERSION = 0;
@ -90,6 +92,13 @@ class Main {
final File file;
final boolean isDir;
Entry(File file, String basename, String entryname) {
this.file = file;
this.isDir = file.isDirectory();
this.basename = basename;
this.entryname = entryname;
}
Entry(int version, File file) {
this.file = file;
String path = file.getPath();
@ -105,6 +114,21 @@ class Main {
entryname = en.entryName;
}
/**
* Returns a new Entry that trims the versions directory.
*
* This entry should be a valid entry matching the given version.
*/
Entry toVersionedEntry(int version) {
assert isValidVersionedEntry(this, version);
if (version == BASE_VERSION)
return this;
EntryName en = new EntryName(trimVersionsDir(basename, version), version);
return new Entry(this.file, en.baseName, en.entryName);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -488,7 +512,9 @@ class Main {
} else if (printModuleDescriptor) {
boolean found;
if (fname != null) {
found = printModuleDescriptor(new ZipFile(fname));
try (ZipFile zf = new ZipFile(fname)) {
found = printModuleDescriptor(zf);
}
} else {
try (FileInputStream fin = new FileInputStream(FileDescriptor.in)) {
found = printModuleDescriptor(fin);
@ -822,21 +848,27 @@ class Main {
return true;
}
private static String toPackageName(ZipEntry entry) {
return toPackageName(entry.getName());
/*
* Add the package of the given resource name if it's a .class
* or a resource in a named package.
*/
boolean addPackageIfNamed(String name) {
if (name.startsWith(VERSIONS_DIR)) {
throw new InternalError(name);
}
String pn = toPackageName(name);
// add if this is a class or resource in a package
if (Checks.isJavaIdentifier(pn)) {
packages.add(pn);
return true;
}
return false;
}
private static String toPackageName(String path) {
assert path.endsWith(".class");
int index;
if (path.startsWith(VERSIONS_DIR)) {
index = path.indexOf('/', VERSIONS_DIR.length());
if (index <= 0) {
return "";
}
path = path.substring(index + 1);
}
index = path.lastIndexOf('/');
int index = path.lastIndexOf('/');
if (index != -1) {
return path.substring(0, index).replace('/', '.');
} else {
@ -844,6 +876,48 @@ class Main {
}
}
/*
* Returns true if the given entry is a valid entry of the given version.
*/
private boolean isValidVersionedEntry(Entry entry, int version) {
String name = entry.basename;
if (name.startsWith(VERSIONS_DIR) && version != BASE_VERSION) {
int i = name.indexOf('/', VERSIONS_DIR.length());
// name == -1 -> not a versioned directory, something else
if (i == -1)
return false;
try {
String v = name.substring(VERSIONS_DIR.length(), i);
return Integer.valueOf(v) == version;
} catch (NumberFormatException x) {
return false;
}
}
return true;
}
/*
* Trim META-INF/versions/$version/ from the given name if the
* given name is a versioned entry of the given version; or
* of any version if the given version is BASE_VERSION
*/
private String trimVersionsDir(String name, int version) {
if (name.startsWith(VERSIONS_DIR)) {
int i = name.indexOf('/', VERSIONS_DIR.length());
if (i >= 0) {
try {
String v = name.substring(VERSIONS_DIR.length(), i);
if (version == BASE_VERSION || Integer.valueOf(v) == version) {
return name.substring(i + 1, name.length());
}
} catch (NumberFormatException x) {}
}
throw new InternalError("unexpected versioned entry: " +
name + " version " + version);
}
return name;
}
/**
* Expands list of files to process into full list of all files that
* can be found by recursively descending directories.
@ -865,28 +939,47 @@ class Main {
else
f = new File(dir, files[i]);
Entry entry = new Entry(version, f);
String entryName = entry.entryname;
Entry e = new Entry(version, f);
String entryName = e.entryname;
Entry entry = e;
if (e.basename.startsWith(VERSIONS_DIR) && isValidVersionedEntry(e, version)) {
entry = e.toVersionedEntry(version);
}
if (f.isFile()) {
if (entryName.endsWith(MODULE_INFO)) {
moduleInfoPaths.put(entryName, f.toPath());
if (isUpdate)
entryMap.put(entryName, entry);
} else if (entries.add(entry)) {
jarEntries.add(entryName);
if (entry.basename.endsWith(".class"))
packages.add(toPackageName(entry.basename));
if (isUpdate)
entryMap.put(entryName, entry);
} else if (isValidVersionedEntry(entry, version)) {
if (entries.add(entry)) {
jarEntries.add(entryName);
// add the package if it's a class or resource
addPackageIfNamed(trimVersionsDir(entry.basename, version));
if (isUpdate)
entryMap.put(entryName, entry);
}
} else {
error(formatMsg2("error.release.unexpected.versioned.entry",
entry.basename, String.valueOf(version)));
ok = false;
}
} else if (f.isDirectory()) {
if (entries.add(entry)) {
if (isUpdate) {
entryMap.put(entryName, entry);
if (isValidVersionedEntry(entry, version)) {
if (entries.add(entry)) {
if (isUpdate) {
entryMap.put(entryName, entry);
}
}
expand(f, f.list(), isUpdate, moduleInfoPaths, version);
} else if (entry.basename.equals(VERSIONS_DIR)) {
if (vflag) {
output(formatMsg("out.ignore.entry", entry.basename));
}
} else {
error(formatMsg2("error.release.unexpected.versioned.entry",
entry.basename, String.valueOf(version)));
ok = false;
}
expand(f, f.list(), isUpdate, moduleInfoPaths, version);
} else {
error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
ok = false;
@ -1047,6 +1140,7 @@ class Main {
} else if (moduleInfos != null && isModuleInfoEntry) {
moduleInfos.putIfAbsent(name, readModuleInfo(zis));
} else {
boolean isDir = e.isDirectory();
if (!entryMap.containsKey(name)) { // copy the old stuff
// do our own compression
ZipEntry e2 = new ZipEntry(name);
@ -1065,11 +1159,14 @@ class Main {
addFile(zos, ent);
entryMap.remove(name);
entries.remove(ent);
isDir = ent.isDir;
}
jarEntries.add(name);
if (name.endsWith(".class"))
packages.add(toPackageName(name));
if (!isDir) {
// add the package if it's a class or resource
addPackageIfNamed(trimVersionsDir(name, BASE_VERSION));
}
}
}
@ -1850,13 +1947,13 @@ class Main {
// Modular jar support
static <T> String toString(Set<T> set,
static <T> String toString(Collection<T> c,
CharSequence prefix,
CharSequence suffix ) {
if (set.isEmpty())
if (c.isEmpty())
return "";
return set.stream().map(e -> e.toString())
return c.stream().map(e -> e.toString())
.collect(joining(", ", prefix, suffix));
}
@ -1890,7 +1987,7 @@ class Main {
return false;
}
static <T> String toString(Set<T> set) {
static <T> String toString(Collection<T> set) {
if (set.isEmpty()) { return ""; }
return set.stream().map(e -> e.toString().toLowerCase(Locale.ROOT))
.collect(joining(" "));
@ -1903,7 +2000,10 @@ class Main {
{
ModuleDescriptor md = ModuleDescriptor.read(entryInputStream);
StringBuilder sb = new StringBuilder();
sb.append("\n").append(md.toNameAndVersion());
sb.append("\n");
if (md.isOpen())
sb.append("open ");
sb.append(md.toNameAndVersion());
md.requires().stream()
.sorted(Comparator.comparing(Requires::name))
@ -1921,10 +2021,17 @@ class Main {
.sorted(Comparator.comparing(Exports::source))
.forEach(p -> sb.append("\n exports ").append(p));
md.conceals().stream().sorted()
.forEach(p -> sb.append("\n conceals ").append(p));
md.opens().stream()
.sorted(Comparator.comparing(Opens::source))
.forEach(p -> sb.append("\n opens ").append(p));
md.provides().values().stream()
Set<String> concealed = new HashSet<>(md.packages());
md.exports().stream().map(Exports::source).forEach(concealed::remove);
md.opens().stream().map(Opens::source).forEach(concealed::remove);
concealed.stream().sorted()
.forEach(p -> sb.append("\n contains ").append(p));
md.provides().stream()
.sorted(Comparator.comparing(Provides::service))
.forEach(p -> sb.append("\n provides ").append(p.service())
.append(" with ")
@ -1957,10 +2064,9 @@ class Main {
{
ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(moduleInfoBytes));
Set<String> missing = md.provides()
.values()
.stream()
.map(Provides::providers)
.flatMap(Set::stream)
.flatMap(List::stream)
.filter(p -> !jarEntries.contains(toBinaryName(p)))
.collect(Collectors.toSet());
if (missing.size() > 0) {
@ -1988,22 +2094,17 @@ class Main {
ModuleDescriptor vd = ModuleDescriptor.read(ByteBuffer.wrap(e.getValue()));
if (!(isValidVersionedDescriptor(vd, rd)))
return false;
e.setValue(extendedInfoBytes(rd, vd, e.getValue(), concealedPackages));
e.setValue(extendedInfoBytes(rd, vd, e.getValue(), packages));
}
return true;
}
private Set<String> findConcealedPackages(ModuleDescriptor md){
private Set<String> findConcealedPackages(ModuleDescriptor md) {
Objects.requireNonNull(md);
Set<String> exports = md.exports()
.stream()
.map(Exports::source)
.collect(toSet());
return packages.stream()
.filter(p -> !exports.contains(p))
.collect(toSet());
Set<String> concealed = new HashSet<>(packages);
md.exports().stream().map(Exports::source).forEach(concealed::remove);
md.opens().stream().map(Opens::source).forEach(concealed::remove);
return concealed;
}
private static boolean isPlatformModule(String name) {
@ -2034,8 +2135,8 @@ class Main {
for (Requires r : vd.requires()) {
if (rootRequires.contains(r)) {
continue;
} else if (r.modifiers().contains(Requires.Modifier.PUBLIC)) {
fatalError(getMsg("error.versioned.info.requires.public"));
} else if (r.modifiers().contains(Requires.Modifier.TRANSITIVE)) {
fatalError(getMsg("error.versioned.info.requires.transitive"));
return false;
} else if (!isPlatformModule(r.name())) {
fatalError(getMsg("error.versioned.info.requires.added"));
@ -2056,6 +2157,10 @@ class Main {
fatalError(getMsg("error.versioned.info.exports.notequal"));
return false;
}
if (!rd.opens().equals(vd.opens())) {
fatalError(getMsg("error.versioned.info.opens.notequal"));
return false;
}
if (!rd.provides().equals(vd.provides())) {
fatalError(getMsg("error.versioned.info.provides.notequal"));
return false;
@ -2074,15 +2179,15 @@ class Main {
private byte[] extendedInfoBytes(ModuleDescriptor rootDescriptor,
ModuleDescriptor md,
byte[] miBytes,
Set<String> conceals)
Set<String> packages)
throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = new ByteArrayInputStream(miBytes);
ModuleInfoExtender extender = ModuleInfoExtender.newExtender(is);
// Add (or replace) the ConcealedPackages attribute
extender.conceals(conceals);
// Add (or replace) the Packages attribute
extender.packages(packages);
// --main-class
if (ename != null)

@ -67,14 +67,16 @@ error.versioned.info.without.root=\
in the root
error.versioned.info.name.notequal=\
module-info.class in a versioned directory contains incorrect name
error.versioned.info.requires.public=\
module-info.class in a versioned directory contains additional "requires public"
error.versioned.info.requires.transitive=\
module-info.class in a versioned directory contains additional "requires transitive"
error.versioned.info.requires.added=\
module-info.class in a versioned directory contains additional "requires"
error.versioned.info.requires.dropped=\
module-info.class in a versioned directory contains missing "requires"
error.versioned.info.exports.notequal=\
module-info.class in a versioned directory contains different "exports"
error.versioned.info.opens.notequal=\
module-info.class in a versioned directory contains different "opens"
error.versioned.info.provides.notequal=\
module-info.class in a versioned directory contains different "provides"
error.invalid.versioned.module.attribute=\
@ -85,6 +87,8 @@ error.release.value.notnumber=\
release {0} not valid
error.release.value.toosmall=\
release {0} not valid, must be >= 9
error.release.unexpected.versioned.entry=\
unexpected versioned entry {0} for release {1}
error.validator.jarfile.exception=\
can not validate {0}: {1}
error.validator.jarfile.invalid=\
@ -104,14 +108,16 @@ error.validator.incompatible.class.version=\
error.validator.different.api=\
entry: {0}, contains a class with different api from earlier version
error.validator.names.mismatch=\
entry: {0}, contains a class with internal name {1}, names do not match
entry: {0}, contains a class with internal name {1}, names do not match
warn.validator.identical.entry=\
warning - entry: {0} contains a class that is identical to an entry already in the jar
Warning: entry {0} contains a class that\n\
is identical to an entry already in the jar
warn.validator.resources.with.same.name=\
warning - entry: {0}, multiple resources with same name
Warning: entry {0}, multiple resources with same name
warn.validator.concealed.public.class=\
warning - entry {0} is a public class in a concealed package, \n\
placing this jar on the class path will result in incompatible public interfaces
Warning: entry {0} is a public class\n\
in a concealed package, placing this jar on the class path will result\n\
in incompatible public interfaces
out.added.manifest=\
added manifest
out.added.module-info=\

@ -24,8 +24,8 @@
*/
module jdk.jconsole {
requires public java.desktop;
requires public java.management;
requires transitive java.desktop;
requires transitive java.management;
requires java.logging;
requires java.rmi;
requires jdk.attach;

@ -30,17 +30,17 @@ package com.sun.jdi;
* A module in the target VM.
* <p>
* Any method on {@code ModuleReference} which directly or
* indirectly takes {@code ModuleReference} as an parameter may throw
* indirectly takes {@code ModuleReference} as a parameter may throw
* {@link com.sun.jdi.VMDisconnectedException} if the target VM is
* disconnected and the {@link com.sun.jdi.event.VMDisconnectEvent} has been or is
* available to be read from the {@link com.sun.jdi.event.EventQueue}.
* <p>
* Any method on {@code ModuleReference} which directly or
* indirectly takes {@code ModuleReference} as an parameter may throw
* indirectly takes {@code ModuleReference} as a parameter may throw
* {@link com.sun.jdi.VMOutOfMemoryException} if the target VM has run out of memory.
* <p>
* Any method on {@code ModuleReference} or which directly or indirectly takes
* {@code ModuleReference} as parameter may throw
* {@code ModuleReference} as a parameter may throw
* {@link com.sun.jdi.InvalidModuleException} if the mirrored module
* has been unloaded.
*
@ -67,12 +67,4 @@ public interface ModuleReference extends ObjectReference {
* @return the {@link ClassLoaderReference} object for this module.
*/
ClassLoaderReference classLoader();
/**
* Indicates if this module reads another module.
*
* @return {@code true} if this module reads {@code other},
* {@code false} otherwise
*/
boolean canRead(ModuleReference other);
}

@ -75,15 +75,4 @@ class ModuleReferenceImpl extends ObjectReferenceImpl implements ModuleReference
}
return classLoader;
}
public synchronized boolean canRead(ModuleReference module) {
boolean ret;
try {
ret = JDWP.ModuleReference.CanRead.
process(this.vm, this, (ModuleReferenceImpl)module).canRead;
} catch (JDWPException ex) {
throw ex.toJDIException();
}
return ret;
}
}

@ -23,6 +23,9 @@
* questions.
*/
/**
* Defines the Java Debugger Interface.
*/
module jdk.jdi {
requires jdk.attach;
requires jdk.jdwp.agent;
@ -37,10 +40,11 @@ module jdk.jdi {
uses com.sun.jdi.connect.spi.TransportService;
// windows shared memory connector providers are added at build time
provides com.sun.jdi.connect.Connector with com.sun.tools.jdi.ProcessAttachingConnector;
provides com.sun.jdi.connect.Connector with com.sun.tools.jdi.RawCommandLineLauncher;
provides com.sun.jdi.connect.Connector with com.sun.tools.jdi.SocketAttachingConnector;
provides com.sun.jdi.connect.Connector with com.sun.tools.jdi.SocketListeningConnector;
provides com.sun.jdi.connect.Connector with com.sun.tools.jdi.SunCommandLineLauncher;
provides com.sun.jdi.connect.Connector with
com.sun.tools.jdi.ProcessAttachingConnector,
com.sun.tools.jdi.RawCommandLineLauncher,
com.sun.tools.jdi.SocketAttachingConnector,
com.sun.tools.jdi.SocketListeningConnector,
com.sun.tools.jdi.SunCommandLineLauncher;
}

@ -23,5 +23,6 @@
* questions.
*/
provides com.sun.jdi.connect.Connector with com.sun.tools.jdi.SharedMemoryAttachingConnector;
provides com.sun.jdi.connect.Connector with com.sun.tools.jdi.SharedMemoryListeningConnector;
provides com.sun.jdi.connect.Connector with
com.sun.tools.jdi.SharedMemoryAttachingConnector,
com.sun.tools.jdi.SharedMemoryListeningConnector;

@ -23,6 +23,9 @@
* questions.
*/
/**
* Java Debug Wire Protocol.
*/
module jdk.jdwp.agent {
}

Some files were not shown because too many files have changed in this diff Show More