8167057: jdeps option to list modules and internal APIs for @modules for test dev

Reviewed-by: dfuchs
This commit is contained in:
Mandy Chung 2016-10-31 18:06:03 -07:00
parent 4f435e9295
commit 97cee45deb
15 changed files with 729 additions and 100 deletions

View File

@ -50,7 +50,7 @@ import static java.util.stream.Collectors.*;
*
* Type of filters:
* source filter: -include <pattern>
* target filter: -package, -regex, -requires
* target filter: -package, -regex, --require
*
* The initial archive set for analysis includes
* 1. archives specified in the command line arguments
@ -146,7 +146,9 @@ public class DepsAnalyzer {
// analyze the dependencies collected
analyzer.run(archives, finder.locationToArchive());
writer.generateOutput(archives, analyzer);
if (writer != null) {
writer.generateOutput(archives, analyzer);
}
} finally {
finder.shutdown();
}
@ -156,7 +158,7 @@ public class DepsAnalyzer {
/**
* Returns the archives for reporting that has matching dependences.
*
* If -requires is set, they should be excluded.
* If --require is set, they should be excluded.
*/
Set<Archive> archives() {
if (filter.requiresFilter().isEmpty()) {
@ -165,7 +167,7 @@ public class DepsAnalyzer {
.filter(Archive::hasDependences)
.collect(Collectors.toSet());
} else {
// use the archives that have dependences and not specified in -requires
// use the archives that have dependences and not specified in --require
return archives.stream()
.filter(filter::include)
.filter(source -> !filter.requiresFilter().contains(source))
@ -360,16 +362,17 @@ public class DepsAnalyzer {
Archive source = dep.originArchive();
Archive target = dep.targetArchive();
String pn = dep.target();
if ((verbose == CLASS || verbose == VERBOSE)) {
if (verbose == CLASS || verbose == VERBOSE) {
int i = dep.target().lastIndexOf('.');
pn = i > 0 ? dep.target().substring(0, i) : "";
}
final Info info;
Module targetModule = target.getModule();
if (source == target) {
info = Info.MODULE_PRIVATE;
} else if (!target.getModule().isNamed()) {
} else if (!targetModule.isNamed()) {
info = Info.EXPORTED_API;
} else if (target.getModule().isExported(pn)) {
} else if (targetModule.isExported(pn) && !targetModule.isJDKUnsupported()) {
info = Info.EXPORTED_API;
} else {
Module module = target.getModule();

View File

@ -41,7 +41,7 @@ import java.util.stream.Stream;
* 2. -filter:package to filter out same-package dependencies
* This filter is applied when jdeps parses the class files
* and filtered dependencies are not stored in the Analyzer.
* 3. -requires specifies to match target dependence from the given module
* 3. --require specifies to match target dependence from the given module
* This gets expanded into package lists to be filtered.
* 4. -filter:archive to filter out same-archive dependencies
* This filter is applied later in the Analyzer as the
@ -166,7 +166,7 @@ public class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
// accepts target that is JDK class but not exported
Module module = targetArchive.getModule();
return originArchive != targetArchive &&
module.isJDK() && !module.isExported(target.getPackageName());
isJDKInternalPackage(module, target.getPackageName());
} else if (filterSameArchive) {
// accepts origin and target that from different archive
return originArchive != targetArchive;
@ -174,6 +174,18 @@ public class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
return true;
}
/**
* Tests if the package is an internal package of the given module.
*/
public boolean isJDKInternalPackage(Module module, String pn) {
if (module.isJDKUnsupported()) {
// its exported APIs are unsupported
return true;
}
return module.isJDK() && !module.isExported(pn);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();

View File

@ -221,6 +221,12 @@ class JdepsTask {
}
}
},
new Option(false, "--list-deps", "--list-reduced-deps") {
void process(JdepsTask task, String opt, String arg) {
task.options.showModulesAddExports = true;
task.options.reduced = opt.equals("--list-reduced-deps");
}
},
// ---- paths option ----
new Option(true, "-cp", "-classpath", "--class-path") {
@ -517,13 +523,13 @@ class JdepsTask {
.forEach(e -> System.out.format("split package: %s %s%n", e.getKey(),
e.getValue().toString()));
// check if any module specified in -requires is missing
// check if any module specified in --require is missing
Stream.concat(options.addmods.stream(), options.requires.stream())
.filter(mn -> !config.isValidToken(mn))
.forEach(mn -> config.findModule(mn).orElseThrow(() ->
new UncheckedBadArgs(new BadArgs("err.module.not.found", mn))));
// --gen-module-info
// --generate-module-info
if (options.genModuleInfo != null) {
return genModuleInfo(config);
}
@ -533,6 +539,13 @@ class JdepsTask {
return new ModuleAnalyzer(config, log, options.checkModuleDeps).run();
}
if (options.showModulesAddExports) {
return new ModuleExportsAnalyzer(config,
dependencyFilter(config),
options.reduced,
log).run();
}
if (options.dotOutputDir != null &&
(options.verbose == SUMMARY || options.verbose == MODULE) &&
!options.addmods.isEmpty() && inputArgs.isEmpty()) {
@ -555,7 +568,7 @@ class JdepsTask {
.appModulePath(options.modulePath)
.addmods(options.addmods);
if (options.checkModuleDeps != null) {
if (options.checkModuleDeps != null || options.showModulesAddExports) {
// check all system modules in the image
builder.allModules();
}
@ -597,10 +610,10 @@ class JdepsTask {
// analyze the dependencies
DepsAnalyzer analyzer = new DepsAnalyzer(config,
dependencyFilter(config),
writer,
options.verbose,
options.apiOnly);
dependencyFilter(config),
writer,
options.verbose,
options.apiOnly);
boolean ok = analyzer.run(options.compileTimeView, options.depth);
@ -727,7 +740,7 @@ class JdepsTask {
* Returns a filter used during dependency analysis
*/
private JdepsFilter dependencyFilter(JdepsConfiguration config) {
// Filter specified by -filter, -package, -regex, and -requires options
// Filter specified by -filter, -package, -regex, and --require options
JdepsFilter.Builder builder = new JdepsFilter.Builder();
// source filters
@ -737,7 +750,7 @@ class JdepsTask {
builder.filter(options.filterSamePackage, options.filterSameArchive);
builder.findJDKInternals(options.findJDKInternals);
// -requires
// --require
if (!options.requires.isEmpty()) {
options.requires.stream()
.forEach(mn -> {
@ -757,8 +770,8 @@ class JdepsTask {
// check if system module is set
config.rootModules().stream()
.map(Module::name)
.forEach(builder::includeIfSystemModule);
.map(Module::name)
.forEach(builder::includeIfSystemModule);
return builder.build();
}
@ -886,6 +899,8 @@ class JdepsTask {
String rootModule;
Set<String> addmods = new HashSet<>();
Runtime.Version multiRelease;
boolean showModulesAddExports;
boolean reduced;
boolean hasFilter() {
return numFilters() > 0;

View File

@ -50,7 +50,7 @@ public abstract class JdepsWriter {
final boolean showProfile;
final boolean showModule;
private JdepsWriter(Analyzer.Type type, boolean showProfile, boolean showModule) {
JdepsWriter(Analyzer.Type type, boolean showProfile, boolean showModule) {
this.type = type;
this.showProfile = showProfile;
this.showModule = showModule;
@ -318,8 +318,7 @@ public abstract class JdepsWriter {
}
// exported API
boolean jdkunsupported = Module.JDK_UNSUPPORTED.equals(module.name());
if (module.isExported(pn) && !jdkunsupported) {
if (module.isExported(pn) && !module.isJDKUnsupported()) {
return showProfileOrModule(module);
}

View File

@ -126,12 +126,13 @@ class Module extends Archive {
* Tests if the package of the given name is exported.
*/
public boolean isExported(String pn) {
if (JDK_UNSUPPORTED.equals(this.name())) {
return false;
}
return exports.containsKey(pn) ? exports.get(pn).isEmpty() : false;
}
public boolean isJDKUnsupported() {
return JDK_UNSUPPORTED.equals(this.name());
}
/**
* Converts this module to a strict module with the given dependences
*

View File

@ -41,10 +41,8 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@ -179,50 +177,43 @@ public class ModuleAnalyzer {
return builder.build();
}
ModuleDescriptor reduced() {
Graph.Builder<Module> bd = new Graph.Builder<>();
private Graph<Module> buildReducedGraph() {
ModuleGraphBuilder rpBuilder = new ModuleGraphBuilder(configuration);
rpBuilder.addModule(root);
requiresPublic.stream()
.forEach(m -> {
bd.addNode(m);
bd.addEdge(root, m);
});
.forEach(m -> rpBuilder.addEdge(root, m));
// requires public graph
Graph<Module> rbg = bd.build().reduce();
Graph<Module> rbg = rpBuilder.build().reduce();
ModuleGraphBuilder gb = new ModuleGraphBuilder(configuration);
gb.addModule(root);
requires.stream()
.forEach(m -> gb.addEdge(root, m));
// transitive reduction
Graph<Module> newGraph = buildGraph(requires).reduce(rbg);
Graph<Module> newGraph = gb.buildGraph().reduce(rbg);
if (DEBUG) {
System.err.println("after transitive reduction: ");
newGraph.printGraph(log);
}
return descriptor(requiresPublic, newGraph.adjacentNodes(root));
return newGraph;
}
/**
* Apply the transitive reduction on the module graph
* and returns the corresponding ModuleDescriptor
*/
ModuleDescriptor reduced() {
Graph<Module> g = buildReducedGraph();
return descriptor(requiresPublic, g.adjacentNodes(root));
}
/**
* Apply transitive reduction on the resulting graph and reports
* recommended requires.
*/
private void analyzeDeps() {
Graph.Builder<Module> builder = new Graph.Builder<>();
requiresPublic.stream()
.forEach(m -> {
builder.addNode(m);
builder.addEdge(root, m);
});
// requires public graph
Graph<Module> rbg = buildGraph(requiresPublic).reduce();
// transitive reduction
Graph<Module> newGraph = buildGraph(requires).reduce(builder.build().reduce());
if (DEBUG) {
System.err.println("after transitive reduction: ");
newGraph.printGraph(log);
}
printModuleDescriptor(log, root);
ModuleDescriptor analyzedDescriptor = descriptor();
@ -276,45 +267,6 @@ public class ModuleAnalyzer {
}
/**
* Returns a graph of modules required by the specified module.
*
* Requires public edges of the dependences are added to the graph.
*/
private Graph<Module> buildGraph(Set<Module> deps) {
Graph.Builder<Module> builder = new Graph.Builder<>();
builder.addNode(root);
Set<Module> visited = new HashSet<>();
visited.add(root);
Deque<Module> deque = new LinkedList<>();
deps.stream()
.forEach(m -> {
deque.add(m);
builder.addEdge(root, m);
});
// read requires public from ModuleDescription
Module source;
while ((source = deque.poll()) != null) {
if (visited.contains(source))
continue;
visited.add(source);
builder.addNode(source);
Module from = source;
source.descriptor().requires().stream()
.filter(req -> req.modifiers().contains(PUBLIC))
.map(ModuleDescriptor.Requires::name)
.map(configuration::findModule)
.flatMap(Optional::stream)
.forEach(m -> {
deque.add(m);
builder.addEdge(from, m);
});
}
return builder.build();
}
/**
* Detects any qualified exports not used by the target module.
*/

View File

@ -0,0 +1,176 @@
/*
* 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 com.sun.tools.jdeps;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.module.ModuleDescriptor;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
/**
* Analyze module dependences and any reference to JDK internal APIs.
* It can apply transition reduction on the resulting module graph.
*
* The result prints one line per module it depends on
* one line per JDK internal API package it references:
* $MODULE[/$PACKAGE]
*
*/
public class ModuleExportsAnalyzer extends DepsAnalyzer {
// source archive to its dependences and JDK internal APIs it references
private final Map<Archive, Map<Archive,Set<String>>> deps = new HashMap<>();
private final boolean reduced;
private final PrintWriter writer;
public ModuleExportsAnalyzer(JdepsConfiguration config,
JdepsFilter filter,
boolean reduced,
PrintWriter writer) {
super(config, filter, null,
Analyzer.Type.PACKAGE,
false /* all classes */);
this.reduced = reduced;
this.writer = writer;
}
@Override
public boolean run() throws IOException {
// analyze dependences
boolean rc = super.run();
// A visitor to record the module-level dependences as well as
// use of JDK internal APIs
Analyzer.Visitor visitor = new Analyzer.Visitor() {
@Override
public void visitDependence(String origin, Archive originArchive,
String target, Archive targetArchive)
{
Set<String> jdkInternals =
deps.computeIfAbsent(originArchive, _k -> new HashMap<>())
.computeIfAbsent(targetArchive, _k -> new HashSet<>());
Module module = targetArchive.getModule();
if (originArchive.getModule() != module &&
module.isJDK() && !module.isExported(target)) {
// use of JDK internal APIs
jdkInternals.add(target);
}
}
};
// visit the dependences
archives.stream()
.filter(analyzer::hasDependences)
.sorted(Comparator.comparing(Archive::getName))
.forEach(archive -> analyzer.visitDependences(archive, visitor));
// print the dependences on named modules
printDependences();
// print the dependences on unnamed module
deps.values().stream()
.flatMap(map -> map.keySet().stream())
.filter(archive -> !archive.getModule().isNamed())
.map(archive -> archive != NOT_FOUND
? "unnamed module: " + archive.getPathName()
: archive.getPathName())
.distinct()
.sorted()
.forEach(archive -> writer.format(" %s%n", archive));
return rc;
}
private void printDependences() {
// find use of JDK internals
Map<Module, Set<String>> jdkinternals = new HashMap<>();
deps.keySet().stream()
.filter(source -> !source.getModule().isNamed())
.map(deps::get)
.flatMap(map -> map.entrySet().stream())
.filter(e -> e.getValue().size() > 0)
.forEach(e -> jdkinternals.computeIfAbsent(e.getKey().getModule(),
_k -> new HashSet<>())
.addAll(e.getValue()));
// build module graph
ModuleGraphBuilder builder = new ModuleGraphBuilder(configuration);
Module root = new RootModule("root");
builder.addModule(root);
// find named module dependences
deps.keySet().stream()
.filter(source -> !source.getModule().isNamed())
.map(deps::get)
.flatMap(map -> map.keySet().stream())
.filter(m -> m.getModule().isNamed())
.map(Archive::getModule)
.forEach(m -> builder.addEdge(root, m));
// module dependences
Set<Module> modules = builder.build().adjacentNodes(root);
// if reduced is set, apply transition reduction
Set<Module> reducedSet = reduced ? builder.reduced().adjacentNodes(root)
: modules;
modules.stream()
.sorted(Comparator.comparing(Module::name))
.forEach(m -> {
if (jdkinternals.containsKey(m)) {
jdkinternals.get(m).stream()
.sorted()
.forEach(pn -> writer.format(" %s/%s%n", m, pn));
} else if (reducedSet.contains(m)){
// if the transition reduction is applied, show the reduced graph
writer.format(" %s%n", m);
}
});
}
private class RootModule extends Module {
final ModuleDescriptor descriptor;
RootModule(String name) {
super(name);
ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(name);
this.descriptor = builder.build();
}
@Override
public ModuleDescriptor descriptor() {
return descriptor;
}
}
}

View File

@ -0,0 +1,130 @@
/*
* 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 com.sun.tools.jdeps;
import java.io.PrintWriter;
import java.lang.module.ModuleDescriptor;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
import static com.sun.tools.jdeps.Module.*;
/**
* A builder to create a Graph<Module>
*/
public class ModuleGraphBuilder extends Graph.Builder<Module> {
final JdepsConfiguration config;
ModuleGraphBuilder(JdepsConfiguration config) {
this.config = config;
}
/**
* Adds a module to the graph.
*/
ModuleGraphBuilder addModule(Module module) {
addNode(module);
return this;
}
/**
* Apply transitive reduction on the resulting graph
*/
public Graph<Module> reduced() {
Graph<Module> graph = build();
// transitive reduction
Graph<Module> newGraph = buildGraph(graph.edges()).reduce();
if (DEBUG) {
PrintWriter log = new PrintWriter(System.err);
System.err.println("before transitive reduction: ");
graph.printGraph(log);
System.err.println("after transitive reduction: ");
newGraph.printGraph(log);
}
return newGraph;
}
public Graph<Module> buildGraph() {
Graph<Module> graph = build();
return buildGraph(graph.edges());
}
/**
* Build a graph of module from the given dependences.
*
* It transitively includes all implied read edges.
*/
private Graph<Module> buildGraph(Map<Module, Set<Module>> edges) {
Graph.Builder<Module> builder = new Graph.Builder<>();
Set<Module> visited = new HashSet<>();
Deque<Module> deque = new LinkedList<>();
edges.entrySet().stream().forEach(e -> {
Module m = e.getKey();
deque.add(m);
e.getValue().stream().forEach(v -> {
deque.add(v);
builder.addEdge(m, v);
});
});
// read requires public from ModuleDescriptor
Module source;
while ((source = deque.poll()) != null) {
if (visited.contains(source))
continue;
visited.add(source);
builder.addNode(source);
Module from = source;
requiresPublic(from).forEach(m -> {
deque.add(m);
builder.addEdge(from, m);
});
}
return builder.build();
}
/*
* Returns a stream of modules upon which the given module `requires public`
*/
public Stream<Module> requiresPublic(Module m) {
// find requires public
return m.descriptor()
.requires().stream()
.filter(req -> req.modifiers().contains(PUBLIC))
.map(ModuleDescriptor.Requires::name)
.map(config::findModule)
.flatMap(Optional::stream);
}
}

View File

@ -149,6 +149,15 @@ main.opt.jdkinternals=\
\ used with -p, -e and -s options.\n\
\ WARNING: JDK internal APIs are inaccessible.
main.opt.list-deps=\
\ --list-deps Lists the dependences and use of JDK internal\n\
\ APIs.\n\
\ --list-reduced-deps Same as --list-deps with not listing\n\
\ the implied reads edges from the module graph\n\
\ If module M1 depends on M2 and M3,\n\
\ M2 requires public on M3, then M1 reading M3 is\n\
\ implied and removed from the module graph.
main.opt.depth=\
\ -depth=<depth> Specify the depth of the transitive\n\
\ dependency analysis

View File

@ -27,16 +27,19 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;
import java.util.spi.ToolProvider;
import java.util.stream.Collectors;
/**
* JdepsRunner class to invoke jdeps with the given command line argument
*/
public class JdepsRunner {
private static final ToolProvider JDEPS_TOOL = ToolProvider.findFirst("jdeps")
.orElseThrow(() -> new RuntimeException("jdeps tool not found"));
public static JdepsRunner run(String... args) {
JdepsRunner jdeps = new JdepsRunner(args);
int rc = jdeps.run();
jdeps.printStdout(System.err);
int rc = jdeps.run(true);
if (rc != 0)
throw new Error("jdeps failed: rc=" + rc);
return jdeps;
@ -46,7 +49,7 @@ public class JdepsRunner {
final StringWriter stderr = new StringWriter();
final String[] args;
public JdepsRunner(String... args) {
System.err.println("jdeps " + Arrays.stream(args)
System.out.println("jdeps " + Arrays.stream(args)
.collect(Collectors.joining(" ")));
this.args = args;
}
@ -60,10 +63,12 @@ public class JdepsRunner {
}
public int run(boolean showOutput) {
try (PrintWriter pw = new PrintWriter(stdout)) {
int rc = com.sun.tools.jdeps.Main.run(args, pw);
try (PrintWriter pwout = new PrintWriter(stdout);
PrintWriter pwerr = new PrintWriter(stderr)) {
int rc = JDEPS_TOOL.run(pwout, pwerr, args);
if (showOutput) {
System.err.println(stdout.toString());
printStdout(System.out);
printStderr(System.out);
}
return rc;
}

View File

@ -0,0 +1,180 @@
/*
* 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.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8167057
* @summary Tests split packages
* @modules java.logging
* java.xml
* jdk.compiler
* jdk.jdeps
* jdk.unsupported
* @library ../lib
* @build CompilerUtils JdepsRunner
* @run testng ListModuleDeps
*/
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
public class ListModuleDeps {
private static final String TEST_SRC = System.getProperty("test.src");
private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
private static final Path CLASSES_DIR = Paths.get("classes");
private static final Path LIB_DIR = Paths.get("lib");
private static final Path FOO_CLASS =
CLASSES_DIR.resolve("z").resolve("Foo.class");
private static final Path BAR_CLASS =
CLASSES_DIR.resolve("z").resolve("Bar.class");
private static final Path UNSAFE_CLASS =
CLASSES_DIR.resolve("z").resolve("UseUnsafe.class");
/**
* Compiles classes used by the test
*/
@BeforeTest
public void compileAll() throws Exception {
// compile library
assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "lib"), LIB_DIR));
// compile classes in unnamed module
assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "z"),
CLASSES_DIR,
"-cp", LIB_DIR.toString(),
"--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
"--add-exports=java.base/sun.security.util=ALL-UNNAMED",
"--add-exports=java.xml/jdk.xml.internal=ALL-UNNAMED"
));
}
@Test(dataProvider = "listdeps")
public void testListDeps(Path classes, String[] expected) {
JdepsRunner jdeps = JdepsRunner.run(
"--class-path", LIB_DIR.toString(),
"--list-deps", classes.toString()
);
String[] output = Arrays.stream(jdeps.output())
.map(s -> s.trim())
.toArray(String[]::new);
assertEquals(output, expected);
}
@Test(dataProvider = "reduceddeps")
public void testListReducedDeps(Path classes, String[] expected) {
JdepsRunner jdeps = JdepsRunner.run(
"--class-path", LIB_DIR.toString(),
"--list-reduced-deps", classes.toString()
);
String[] output = Arrays.stream(jdeps.output())
.map(s -> s.trim())
.toArray(String[]::new);
assertEquals(output, expected);
}
@DataProvider(name = "listdeps")
public Object[][] listdeps() {
return new Object[][] {
{ CLASSES_DIR, new String[] {
"java.base/jdk.internal.misc",
"java.base/sun.security.util",
"java.logging",
"java.sql",
"java.xml/jdk.xml.internal",
"jdk.unsupported",
"unnamed module: lib"
}
},
{ FOO_CLASS, new String[] {
"java.base",
"java.logging",
"java.sql",
"java.xml",
"unnamed module: lib"
}
},
{ BAR_CLASS, new String[] {
"java.base/sun.security.util",
"java.xml/jdk.xml.internal",
}
},
{ UNSAFE_CLASS, new String[] {
"java.base/jdk.internal.misc",
"jdk.unsupported",
}
},
};
}
@DataProvider(name = "reduceddeps")
public Object[][] reduceddeps() {
Path barClass = CLASSES_DIR.resolve("z").resolve("Bar.class");
return new Object[][] {
{ CLASSES_DIR, new String[] {
"java.base/jdk.internal.misc",
"java.base/sun.security.util",
"java.sql",
"java.xml/jdk.xml.internal",
"jdk.unsupported",
"unnamed module: lib"
}
},
{ FOO_CLASS, new String[] {
"java.base",
"java.sql",
"unnamed module: lib"
}
},
{ BAR_CLASS, new String[] {
"java.base/sun.security.util",
"java.xml/jdk.xml.internal",
}
},
{ UNSAFE_CLASS, new String[] {
"java.base/jdk.internal.misc",
"jdk.unsupported",
}
},
};
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.
*
* 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 lib;
import javax.xml.stream.XMLInputFactory;
public class Lib {
public static final String isCoalescing = XMLInputFactory.IS_COALESCING;
public static boolean check() { return true; }
}

View File

@ -0,0 +1,36 @@
/*
* 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.
*
* 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 z;
import sun.security.util.HostnameChecker;
import jdk.xml.internal.JdkXmlUtils;
public class Bar {
// internal API from java.xml
private static final String name = JdkXmlUtils.USE_CATALOG;
public static void main(String[] argv) throws Exception {
HostnameChecker hc = HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP);
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.
*
* 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 z;
import java.sql.Driver;
import java.util.logging.Logger;
import javax.xml.stream.XMLInputFactory;
/*
* Dependences on java.sql and java.logging which can be reduced.
*/
public class Foo {
// dependence to java.logging
static Logger log = Logger.getLogger("foo");
static final String isCoalescing = XMLInputFactory.IS_COALESCING;
// dependence to java.sql
public Driver getDriver() { return null; }
// dependence to same package
public Bar getBar() { return new Bar(); }
// dependence to module m
public String isCoalescing() { return lib.Lib.isCoalescing; }
}

View File

@ -0,0 +1,33 @@
/*
* 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.
*
* 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 z;
import sun.misc.Unsafe;
import jdk.internal.misc.VM;
public class UseUnsafe {
private static Unsafe unsafe = Unsafe.getUnsafe();
private static boolean booted = VM.isBooted();
}