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:
parent
2e931825c6
commit
fbe85300bf
jdk
make
data/jdwp
launcher
src/classes/build/tools
src
java.base/share
classes
com/sun/java/util/jar/pack
java
lang
Class.javaClassLoader.javaDeprecated.javaPackage.javaSuppressWarnings.javaSystem.javaVersionProps.java.template
annotation
invoke
module
Configuration.javaModuleDescriptor.javaModuleFinder.javaModuleInfo.javaModulePath.javaModuleReference.javaModuleReferences.javaResolver.javaSystemModuleFinder.java
reflect
util
jdk/internal
loader
misc
JavaLangAccess.javaJavaLangModuleAccess.javaJavaLangReflectModuleAccess.javaJavaUtilResourceBundleAccess.javaSharedSecrets.java
module
Builder.javaChecks.javaClassFileAttributes.javaClassFileConstants.javaModuleBootstrap.javaModuleHashes.javaModuleInfoExtender.javaModuleInfoWriter.javaModulePatcher.javaModules.javaServicesCatalog.javaSystemModules.java
org/objectweb/asm
reflect
sun
launcher
security/util
util
native
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
share/classes
windows/classes
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> with %s",
|
||||
e.getKey(), p)))
|
||||
ms.descriptor().provides().stream()
|
||||
.sorted(Comparator.comparing(Provides::service))
|
||||
.map(p -> String.format("provides %s<br> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
243
jdk/make/src/classes/build/tools/module/ModuleInfoExtraTest.java
Normal file
243
jdk/make/src/classes/build/tools/module/ModuleInfoExtraTest.java
Normal file
@ -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
|
||||
* "<init>"or "<clinit>".
|
||||
*/
|
||||
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", "", "XX")} and {@code Locale("en")}
|
||||
* could be {@code "p.resources.ja.Bundle_ja_ _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™ 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
Loading…
x
Reference in New Issue
Block a user