diff --git a/langtools/make/build.properties b/langtools/make/build.properties
index 38939fa856f..c65ccb64bd1 100644
--- a/langtools/make/build.properties
+++ b/langtools/make/build.properties
@@ -28,6 +28,10 @@ javac.opts = -XDignore.symbol.file=true -Xlint:all,-deprecation,-options -Werror
javac.source = 9
javac.target = 9
+#version used to compile build tools
+javac.build.source = 8
+javac.build.target = 8
+
langtools.resource.includes = \
com/sun/tools/javac/resources/compiler.properties
diff --git a/langtools/make/build.xml b/langtools/make/build.xml
index e96ec89a819..564c2b5299a 100644
--- a/langtools/make/build.xml
+++ b/langtools/make/build.xml
@@ -239,11 +239,12 @@
-
-
-
+
+
-
@@ -255,10 +256,8 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/langtools/make/intellij/runConfigurations/javadoc.xml b/langtools/make/intellij/runConfigurations/javadoc.xml
new file mode 100644
index 00000000000..159a471b406
--- /dev/null
+++ b/langtools/make/intellij/runConfigurations/javadoc.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/langtools/make/intellij/runConfigurations/javah.xml b/langtools/make/intellij/runConfigurations/javah.xml
new file mode 100644
index 00000000000..4f7003db88e
--- /dev/null
+++ b/langtools/make/intellij/runConfigurations/javah.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/langtools/make/intellij/runConfigurations/javap.xml b/langtools/make/intellij/runConfigurations/javap.xml
new file mode 100644
index 00000000000..8e3caf268d7
--- /dev/null
+++ b/langtools/make/intellij/runConfigurations/javap.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/langtools/make/intellij/runConfigurations/jshell.xml b/langtools/make/intellij/runConfigurations/jshell.xml
new file mode 100644
index 00000000000..36e0cedb8d1
--- /dev/null
+++ b/langtools/make/intellij/runConfigurations/jshell.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/langtools/make/intellij/runConfigurations/jtreg__debug_.xml b/langtools/make/intellij/runConfigurations/jtreg__debug_.xml
new file mode 100644
index 00000000000..fd1bdccf9f3
--- /dev/null
+++ b/langtools/make/intellij/runConfigurations/jtreg__debug_.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/langtools/make/intellij/runConfigurations/jtreg__run_.xml b/langtools/make/intellij/runConfigurations/jtreg__run_.xml
new file mode 100644
index 00000000000..b9f655f8595
--- /dev/null
+++ b/langtools/make/intellij/runConfigurations/jtreg__run_.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/langtools/make/intellij/runConfigurations/sjavac.xml b/langtools/make/intellij/runConfigurations/sjavac.xml
new file mode 100644
index 00000000000..d310b1a3e05
--- /dev/null
+++ b/langtools/make/intellij/runConfigurations/sjavac.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/langtools/make/intellij/workspace.xml b/langtools/make/intellij/workspace.xml
index e0e0c579829..babc8a4e957 100644
--- a/langtools/make/intellij/workspace.xml
+++ b/langtools/make/intellij/workspace.xml
@@ -1,157 +1,5 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/langtools/make/tools/crules/AssertCheckAnalyzer.java b/langtools/make/tools/crules/AssertCheckAnalyzer.java
index d46b5e13d65..b665d74215e 100644
--- a/langtools/make/tools/crules/AssertCheckAnalyzer.java
+++ b/langtools/make/tools/crules/AssertCheckAnalyzer.java
@@ -37,6 +37,9 @@ import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.Assert;
+/**This analyzer guards against complex messages (i.e. those that use string concatenation) passed
+ * to various Assert.check methods.
+ */
public class AssertCheckAnalyzer extends AbstractCodingRulesAnalyzer {
enum AssertOverloadKind {
diff --git a/langtools/make/tools/crules/MutableFieldsAnalyzer.java b/langtools/make/tools/crules/MutableFieldsAnalyzer.java
index a992f55884b..defb98760a1 100644
--- a/langtools/make/tools/crules/MutableFieldsAnalyzer.java
+++ b/langtools/make/tools/crules/MutableFieldsAnalyzer.java
@@ -41,6 +41,7 @@ import static com.sun.tools.javac.code.Flags.STATIC;
import static com.sun.tools.javac.code.Flags.SYNTHETIC;
import static com.sun.tools.javac.code.Kinds.Kind.*;
+/**This analyzer guards against non-final static fields.*/
public class MutableFieldsAnalyzer extends AbstractCodingRulesAnalyzer {
public MutableFieldsAnalyzer(JavacTask task) {
diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java
index 7ce6ccd17ec..7030511e9dd 100644
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java
@@ -365,18 +365,22 @@ public class ArgumentAttr extends JCTree.Visitor {
@Override
Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
- for (Map.Entry _entry : speculativeTypes.entrySet()) {
- DeferredAttrContext deferredAttrContext = _entry.getKey().checkContext.deferredAttrContext();
- if (deferredAttrContext.phase == phase && deferredAttrContext.msym == msym) {
- return _entry.getValue();
+ if (pertinentToApplicability) {
+ for (Map.Entry _entry : speculativeTypes.entrySet()) {
+ DeferredAttrContext deferredAttrContext = _entry.getKey().checkContext.deferredAttrContext();
+ if (deferredAttrContext.phase == phase && deferredAttrContext.msym == msym) {
+ return _entry.getValue();
+ }
}
+ return Type.noType;
+ } else {
+ return super.speculativeType(msym, phase);
}
- return Type.noType;
}
@Override
JCTree speculativeTree(DeferredAttrContext deferredAttrContext) {
- return speculativeTree;
+ return pertinentToApplicability ? speculativeTree : super.speculativeTree(deferredAttrContext);
}
/**
diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
index 1fd1d379a74..7dcdc4caec2 100644
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -2678,17 +2678,31 @@ public class Attr extends JCTree.Visitor {
class ExpressionLambdaReturnContext extends FunctionalReturnContext {
JCExpression expr;
+ boolean expStmtExpected;
ExpressionLambdaReturnContext(JCExpression expr, CheckContext enclosingContext) {
super(enclosingContext);
this.expr = expr;
}
+ @Override
+ public void report(DiagnosticPosition pos, JCDiagnostic details) {
+ if (expStmtExpected) {
+ enclosingContext.report(pos, diags.fragment(Fragments.StatExprExpected));
+ } else {
+ super.report(pos, details);
+ }
+ }
+
@Override
public boolean compatible(Type found, Type req, Warner warn) {
//a void return is compatible with an expression statement lambda
- return TreeInfo.isExpressionStatement(expr) && req.hasTag(VOID) ||
- super.compatible(found, req, warn);
+ if (req.hasTag(VOID)) {
+ expStmtExpected = true;
+ return TreeInfo.isExpressionStatement(expr);
+ } else {
+ return super.compatible(found, req, warn);
+ }
}
}
diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java
index c588ecd3e1d..fa861b07c9a 100644
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java
@@ -35,6 +35,7 @@ import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.DefinedBy.Api;
+import com.sun.tools.javac.util.GraphUtils.DependencyKind;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.comp.Attr.ResultInfo;
@@ -44,9 +45,10 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
-import java.util.LinkedHashMap;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@@ -172,6 +174,7 @@ public class DeferredAttr extends JCTree.Visitor {
public JCExpression tree;
Env env;
AttrMode mode;
+ boolean pertinentToApplicability = true;
SpeculativeCache speculativeCache;
DeferredType(JCExpression tree, Env env) {
@@ -290,6 +293,7 @@ public class DeferredAttr extends JCTree.Visitor {
resultInfo.checkContext.deferredAttrContext();
Assert.check(deferredAttrContext != emptyDeferredAttrContext);
if (deferredStuckPolicy.isStuck()) {
+ pertinentToApplicability = false;
deferredAttrContext.addDeferredAttrNode(this, resultInfo, deferredStuckPolicy);
return Type.noType;
} else {
@@ -574,28 +578,11 @@ public class DeferredAttr extends JCTree.Visitor {
*/
void complete() {
while (!deferredAttrNodes.isEmpty()) {
- Map> depVarsMap = new LinkedHashMap<>();
- List stuckVars = List.nil();
boolean progress = false;
//scan a defensive copy of the node list - this is because a deferred
//attribution round can add new nodes to the list
for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
- if (!deferredAttrNode.process(this)) {
- List restStuckVars =
- List.from(deferredAttrNode.deferredStuckPolicy.stuckVars())
- .intersect(inferenceContext.restvars());
- stuckVars = stuckVars.prependList(restStuckVars);
- //update dependency map
- for (Type t : List.from(deferredAttrNode.deferredStuckPolicy.depVars())
- .intersect(inferenceContext.restvars())) {
- Set prevDeps = depVarsMap.get(t);
- if (prevDeps == null) {
- prevDeps = new LinkedHashSet<>();
- depVarsMap.put(t, prevDeps);
- }
- prevDeps.addAll(restStuckVars);
- }
- } else {
+ if (deferredAttrNode.process(this)) {
deferredAttrNodes.remove(deferredAttrNode);
progress = true;
}
@@ -610,7 +597,9 @@ public class DeferredAttr extends JCTree.Visitor {
//remove all variables that have already been instantiated
//from the list of stuck variables
try {
- inferenceContext.solveAny(stuckVars, depVarsMap, warn);
+ //find stuck expression to unstuck
+ DeferredAttrNode toUnstuck = pickDeferredNode();
+ inferenceContext.solveAny(List.from(toUnstuck.deferredStuckPolicy.stuckVars()), warn);
inferenceContext.notifyChange();
} catch (Infer.GraphStrategy.NodeNotFoundException ex) {
//this means that we are in speculative mode and the
@@ -632,6 +621,59 @@ public class DeferredAttr extends JCTree.Visitor {
}
return dac.parent.insideOverloadPhase();
}
+
+ /**
+ * Pick the deferred node to be unstuck. The chosen node is the first strongly connected
+ * component containing exactly one node found in the dependency graph induced by deferred nodes.
+ * If no such component is found, the first deferred node is returned.
+ */
+ DeferredAttrNode pickDeferredNode() {
+ List nodes = deferredAttrNodes.stream()
+ .map(StuckNode::new)
+ .collect(List.collector());
+ //init stuck expression graph; a deferred node A depends on a deferred node B iff
+ //the intersection between A's input variable and B's output variable is non-empty.
+ for (StuckNode sn1 : nodes) {
+ for (Type t : sn1.data.deferredStuckPolicy.stuckVars()) {
+ for (StuckNode sn2 : nodes) {
+ if (sn1 != sn2 && sn2.data.deferredStuckPolicy.depVars().contains(t)) {
+ sn1.deps.add(sn2);
+ }
+ }
+ }
+ }
+ //compute tarjan on the stuck graph
+ List extends StuckNode> csn = GraphUtils.tarjan(nodes).get(0);
+ return csn.length() == 1 ? csn.get(0).data : deferredAttrNodes.get(0);
+ }
+
+ class StuckNode extends GraphUtils.TarjanNode {
+
+ Set deps = new HashSet<>();
+
+ StuckNode(DeferredAttrNode data) {
+ super(data);
+ }
+
+ @Override
+ public DependencyKind[] getSupportedDependencyKinds() {
+ return new DependencyKind[] { Infer.DependencyKind.STUCK };
+ }
+
+ @Override
+ public Collection extends StuckNode> getDependenciesByKind(DependencyKind dk) {
+ if (dk == Infer.DependencyKind.STUCK) {
+ return deps;
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ @Override
+ public Iterable extends StuckNode> getAllDependencies() {
+ return deps;
+ }
+ }
}
/**
diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
index 776a1bfc18c..d0e49a2d10c 100644
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
@@ -2345,10 +2345,11 @@ public class Flow {
// assigned before reading their value
public void visitSelect(JCFieldAccess tree) {
super.visitSelect(tree);
+ JCTree sel = TreeInfo.skipParens(tree.selected);
if (enforceThisDotInit &&
- tree.selected.hasTag(IDENT) &&
- ((JCIdent)tree.selected).name == names._this &&
- tree.sym.kind == VAR) {
+ sel.hasTag(IDENT) &&
+ ((JCIdent)sel).name == names._this &&
+ tree.sym.kind == VAR) {
checkInit(tree.pos(), (VarSymbol)tree.sym);
}
}
diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java
index 87026e6e4fe..d028653d94e 100644
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java
@@ -52,11 +52,9 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -1655,12 +1653,10 @@ public class Infer {
class GraphSolver {
InferenceContext inferenceContext;
- Map> stuckDeps;
Warner warn;
- GraphSolver(InferenceContext inferenceContext, Map> stuckDeps, Warner warn) {
+ GraphSolver(InferenceContext inferenceContext, Warner warn) {
this.inferenceContext = inferenceContext;
- this.stuckDeps = stuckDeps;
this.warn = warn;
}
@@ -1671,7 +1667,7 @@ public class Infer {
*/
void solve(GraphStrategy sstrategy) {
doIncorporation(inferenceContext, warn); //initial propagation of bounds
- InferenceGraph inferenceGraph = new InferenceGraph(stuckDeps);
+ InferenceGraph inferenceGraph = new InferenceGraph();
while (!sstrategy.done()) {
if (dependenciesFolder != null) {
//add this graph to the pending queue
@@ -1720,12 +1716,12 @@ public class Infer {
*/
class Node extends GraphUtils.TarjanNode, Node> implements DottableNode, Node> {
- /** map listing all dependencies (grouped by kind) */
- EnumMap> deps;
+ /** node dependencies */
+ Set deps;
Node(Type ivar) {
super(ListBuffer.of(ivar));
- this.deps = new EnumMap<>(DependencyKind.class);
+ this.deps = new HashSet<>();
}
@Override
@@ -1734,76 +1730,53 @@ public class Infer {
}
public Iterable extends Node> getAllDependencies() {
- return getDependencies(DependencyKind.values());
+ return deps;
}
@Override
public Collection extends Node> getDependenciesByKind(GraphUtils.DependencyKind dk) {
- return getDependencies((DependencyKind)dk);
- }
-
- /**
- * Retrieves all dependencies with given kind(s).
- */
- protected Set getDependencies(DependencyKind... depKinds) {
- Set buf = new LinkedHashSet<>();
- for (DependencyKind dk : depKinds) {
- Set depsByKind = deps.get(dk);
- if (depsByKind != null) {
- buf.addAll(depsByKind);
- }
+ if (dk == DependencyKind.BOUND) {
+ return deps;
+ } else {
+ throw new IllegalStateException();
}
- return buf;
}
/**
* Adds dependency with given kind.
*/
- protected void addDependency(DependencyKind dk, Node depToAdd) {
- Set depsByKind = deps.get(dk);
- if (depsByKind == null) {
- depsByKind = new LinkedHashSet<>();
- deps.put(dk, depsByKind);
- }
- depsByKind.add(depToAdd);
+ protected void addDependency(Node depToAdd) {
+ deps.add(depToAdd);
}
/**
* Add multiple dependencies of same given kind.
*/
- protected void addDependencies(DependencyKind dk, Set depsToAdd) {
+ protected void addDependencies(Set depsToAdd) {
for (Node n : depsToAdd) {
- addDependency(dk, n);
+ addDependency(n);
}
}
/**
* Remove a dependency, regardless of its kind.
*/
- protected Set removeDependency(Node n) {
- Set removedKinds = new HashSet<>();
- for (DependencyKind dk : DependencyKind.values()) {
- Set depsByKind = deps.get(dk);
- if (depsByKind == null) continue;
- if (depsByKind.remove(n)) {
- removedKinds.add(dk);
- }
- }
- return removedKinds;
+ protected boolean removeDependency(Node n) {
+ return deps.remove(n);
}
/**
* Compute closure of a give node, by recursively walking
* through all its dependencies (of given kinds)
*/
- protected Set closure(DependencyKind... depKinds) {
+ protected Set closure() {
boolean progress = true;
Set closure = new HashSet<>();
closure.add(this);
while (progress) {
progress = false;
for (Node n1 : new HashSet<>(closure)) {
- progress = closure.addAll(n1.getDependencies(depKinds));
+ progress = closure.addAll(n1.deps);
}
}
return closure;
@@ -1815,9 +1788,8 @@ public class Infer {
*/
protected boolean isLeaf() {
//no deps, or only one self dep
- Set allDeps = getDependencies(DependencyKind.BOUND, DependencyKind.STUCK);
- if (allDeps.isEmpty()) return true;
- for (Node n : allDeps) {
+ if (deps.isEmpty()) return true;
+ for (Node n : deps) {
if (n != this) {
return false;
}
@@ -1834,24 +1806,15 @@ public class Infer {
for (Node n : nodes) {
Assert.check(n.data.length() == 1, "Attempt to merge a compound node!");
data.appendList(n.data);
- for (DependencyKind dk : DependencyKind.values()) {
- addDependencies(dk, n.getDependencies(dk));
- }
+ addDependencies(n.deps);
}
//update deps
- EnumMap> deps2 = new EnumMap<>(DependencyKind.class);
- for (DependencyKind dk : DependencyKind.values()) {
- for (Node d : getDependencies(dk)) {
- Set depsByKind = deps2.get(dk);
- if (depsByKind == null) {
- depsByKind = new LinkedHashSet<>();
- deps2.put(dk, depsByKind);
- }
- if (data.contains(d.data.first())) {
- depsByKind.add(this);
- } else {
- depsByKind.add(d);
- }
+ Set deps2 = new HashSet<>();
+ for (Node d : deps) {
+ if (data.contains(d.data.first())) {
+ deps2.add(this);
+ } else {
+ deps2.add(d);
}
}
deps = deps2;
@@ -1862,9 +1825,9 @@ public class Infer {
* topology.
*/
private void graphChanged(Node from, Node to) {
- for (DependencyKind dk : removeDependency(from)) {
+ if (removeDependency(from)) {
if (to != null) {
- addDependency(dk, to);
+ addDependency(to);
}
}
}
@@ -1880,22 +1843,19 @@ public class Infer {
public Properties dependencyAttributes(Node sink, GraphUtils.DependencyKind dk) {
Properties p = new Properties();
p.put("style", ((DependencyKind)dk).dotSyle);
- if (dk == DependencyKind.STUCK) return p;
- else {
- StringBuilder buf = new StringBuilder();
- String sep = "";
- for (Type from : data) {
- UndetVar uv = (UndetVar)inferenceContext.asUndetVar(from);
- for (Type bound : uv.getBounds(InferenceBound.values())) {
- if (bound.containsAny(List.from(sink.data))) {
- buf.append(sep);
- buf.append(bound);
- sep = ",";
- }
+ StringBuilder buf = new StringBuilder();
+ String sep = "";
+ for (Type from : data) {
+ UndetVar uv = (UndetVar)inferenceContext.asUndetVar(from);
+ for (Type bound : uv.getBounds(InferenceBound.values())) {
+ if (bound.containsAny(List.from(sink.data))) {
+ buf.append(sep);
+ buf.append(bound);
+ sep = ",";
}
}
- p.put("label", "\"" + buf.toString() + "\"");
}
+ p.put("label", "\"" + buf.toString() + "\"");
return p;
}
}
@@ -1903,8 +1863,8 @@ public class Infer {
/** the nodes in the inference graph */
ArrayList nodes;
- InferenceGraph(Map> optDeps) {
- initNodes(optDeps);
+ InferenceGraph() {
+ initNodes();
}
/**
@@ -1946,7 +1906,7 @@ public class Infer {
* in the graph. For each component containing more than one node, a super node is
* created, effectively replacing the original cyclic nodes.
*/
- void initNodes(Map> stuckDeps) {
+ void initNodes() {
//add nodes
nodes = new ArrayList<>();
for (Type t : inferenceContext.restvars()) {
@@ -1955,17 +1915,12 @@ public class Infer {
//add dependencies
for (Node n_i : nodes) {
Type i = n_i.data.first();
- Set optDepsByNode = stuckDeps.get(i);
for (Node n_j : nodes) {
Type j = n_j.data.first();
UndetVar uv_i = (UndetVar)inferenceContext.asUndetVar(i);
if (Type.containsAny(uv_i.getBounds(InferenceBound.values()), List.of(j))) {
//update i's bound dependencies
- n_i.addDependency(DependencyKind.BOUND, n_j);
- }
- if (optDepsByNode != null && optDepsByNode.contains(j)) {
- //update i's stuck dependencies
- n_i.addDependency(DependencyKind.STUCK, n_j);
+ n_i.addDependency(n_j);
}
}
}
diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java
index 398e291bdbb..f44cdc53d9a 100644
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java
@@ -469,15 +469,11 @@ class InferenceContext {
}
}
- private void solve(GraphStrategy ss, Warner warn) {
- solve(ss, new HashMap>(), warn);
- }
-
/**
* Solve with given graph strategy.
*/
- private void solve(GraphStrategy ss, Map> stuckDeps, Warner warn) {
- GraphSolver s = infer.new GraphSolver(this, stuckDeps, warn);
+ private void solve(GraphStrategy ss, Warner warn) {
+ GraphSolver s = infer.new GraphSolver(this, warn);
s.solve(ss);
}
@@ -506,12 +502,12 @@ class InferenceContext {
/**
* Solve at least one variable in given list.
*/
- public void solveAny(List varsToSolve, Map> optDeps, Warner warn) {
+ public void solveAny(List varsToSolve, Warner warn) {
solve(infer.new BestLeafSolver(varsToSolve.intersect(restvars())) {
public boolean done() {
return instvars().intersect(varsToSolve).nonEmpty();
}
- }, optDeps, warn);
+ }, warn);
}
/**
diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java
index 1c84b51a0db..899365f28e2 100644
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java
@@ -380,13 +380,17 @@ public abstract class BaseFileManager implements JavaFileManager {
dest = CharBuffer.allocate(newCapacity).put(dest);
} else if (result.isMalformed() || result.isUnmappable()) {
// bad character in input
+ StringBuilder unmappable = new StringBuilder();
+ int len = result.length();
- log.error(new SimpleDiagnosticPosition(dest.limit()),
- "illegal.char.for.encoding",
- charset == null ? encodingName : charset.name());
+ for (int i = 0; i < len; i++) {
+ unmappable.append(String.format("%02X", inbuf.get()));
+ }
- // skip past the coding error
- inbuf.position(inbuf.position() + result.length());
+ String charsetName = charset == null ? encodingName : charset.name();
+
+ log.error(dest.limit(),
+ Errors.IllegalCharForEncoding(unmappable.toString(), charsetName));
// undo the flip() to prepare the output buffer
// for more translation
diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java
index 12106e3ecc1..015690466cd 100644
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java
@@ -1138,7 +1138,7 @@ public class Locations {
}
}
- if (warn && false) { // temp disable
+ if (warn && false) { // temp disable, when enabled, massage examples.not-yet.txt suitably.
log.warning(Warnings.LocnUnknownFileOnModulePath(p));
}
return null;
diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
index a531e1793c0..3d6169d4343 100644
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
@@ -522,8 +522,9 @@ compiler.err.icls.cant.have.static.decl=\
compiler.err.illegal.char=\
illegal character: ''{0}''
+# 0: string, 1: string
compiler.err.illegal.char.for.encoding=\
- unmappable character for encoding {0}
+ unmappable character (0x{0}) for encoding {1}
# 0: set of modifier, 1: set of modifier
compiler.err.illegal.combination.of.modifiers=\
@@ -779,6 +780,10 @@ compiler.misc.incompatible.ret.type.in.lambda=\
bad return type in lambda expression\n\
{0}
+compiler.misc.stat.expr.expected=\
+ lambda body is not compatible with a void functional interface\n\
+ (consider using a block lambda body, or use a statement expression instead)
+
# 0: type
compiler.misc.incompatible.ret.type.in.mref=\
bad return type in method reference\n\
@@ -2747,9 +2752,6 @@ compiler.err.module.name.mismatch=\
compiler.err.module.decl.sb.in.module-info.java=\
module declarations should be in a file named module-info.java
-compiler.err.unexpected.after.module=\
- unexpected input after module declaration
-
compiler.err.module-info.with.xmodule.sourcepath=\
illegal combination of -Xmodule and module-info on sourcepath
diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java
index f86f874cba4..9ab985b8091 100644
--- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java
@@ -714,43 +714,43 @@ public abstract class Configuration {
}
public String getText(String key) {
- try {
- //Check the doclet specific properties file.
- return getDocletSpecificMsg().getText(key);
- } catch (Exception e) {
- //Check the shared properties file.
- return message.getText(key);
+ // Check the doclet specific properties file.
+ MessageRetriever docletMessage = getDocletSpecificMsg();
+ if (docletMessage.containsKey(key)) {
+ return docletMessage.getText(key);
}
+ // Check the shared properties file.
+ return message.getText(key);
}
public String getText(String key, String a1) {
- try {
- //Check the doclet specific properties file.
- return getDocletSpecificMsg().getText(key, a1);
- } catch (Exception e) {
- //Check the shared properties file.
- return message.getText(key, a1);
+ // Check the doclet specific properties file.
+ MessageRetriever docletMessage = getDocletSpecificMsg();
+ if (docletMessage.containsKey(key)) {
+ return docletMessage.getText(key, a1);
}
+ // Check the shared properties file.
+ return message.getText(key, a1);
}
public String getText(String key, String a1, String a2) {
- try {
- //Check the doclet specific properties file.
- return getDocletSpecificMsg().getText(key, a1, a2);
- } catch (Exception e) {
- //Check the shared properties file.
- return message.getText(key, a1, a2);
+ // Check the doclet specific properties file.
+ MessageRetriever docletMessage = getDocletSpecificMsg();
+ if (docletMessage.containsKey(key)) {
+ return docletMessage.getText(key, a1, a2);
}
+ // Check the shared properties file.
+ return message.getText(key, a1, a2);
}
public String getText(String key, String a1, String a2, String a3) {
- try {
- //Check the doclet specific properties file.
- return getDocletSpecificMsg().getText(key, a1, a2, a3);
- } catch (Exception e) {
- //Check the shared properties file.
- return message.getText(key, a1, a2, a3);
+ // Check the doclet specific properties file.
+ MessageRetriever docletMessage = getDocletSpecificMsg();
+ if (docletMessage.containsKey(key)) {
+ return docletMessage.getText(key, a1, a2, a3);
}
+ // Check the shared properties file.
+ return message.getText(key, a1, a2, a3);
}
public abstract Content newContent();
diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/MessageRetriever.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/MessageRetriever.java
index e357556d5bf..094e6997660 100644
--- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/MessageRetriever.java
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/MessageRetriever.java
@@ -83,6 +83,34 @@ public class MessageRetriever {
this.resourcelocation = resourcelocation;
}
+ private ResourceBundle initRB() {
+ ResourceBundle bundle = messageRB;
+ if (bundle == null) {
+ try {
+ messageRB = bundle =
+ ResourceBundle.getBundle(resourcelocation, configuration.getLocale());
+ } catch (MissingResourceException e) {
+ throw new Error("Fatal: Resource (" + resourcelocation
+ + ") for javadoc doclets is missing.");
+ }
+ }
+ return bundle;
+ }
+
+ /**
+ * Determines whether the given key can be retrieved
+ * from this MessageRetriever
+ *
+ * @param key
+ * the resource key
+ * @return true if the given key is
+ * contained in the underlying ResourceBundle.
+ */
+ public boolean containsKey(String key) {
+ ResourceBundle bundle = initRB();
+ return bundle.containsKey(key);
+ }
+
/**
* Get and format message string from resource
*
@@ -92,15 +120,8 @@ public class MessageRetriever {
* exist in the properties file.
*/
public String getText(String key, Object... args) throws MissingResourceException {
- if (messageRB == null) {
- try {
- messageRB = ResourceBundle.getBundle(resourcelocation);
- } catch (MissingResourceException e) {
- throw new Error("Fatal: Resource (" + resourcelocation +
- ") for javadoc doclets is missing.");
- }
- }
- String message = messageRB.getString(key);
+ ResourceBundle bundle = initRB();
+ String message = bundle.getString(key);
return MessageFormat.format(message, args);
}
diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Configuration.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Configuration.java
index 34eced11854..96f55499fdc 100644
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Configuration.java
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Configuration.java
@@ -894,43 +894,43 @@ public abstract class Configuration {
}
public String getText(String key) {
- try {
- //Check the doclet specific properties file.
- return getDocletSpecificMsg().getText(key);
- } catch (Exception e) {
- //Check the shared properties file.
- return message.getText(key);
+ // Check the doclet specific properties file.
+ MessageRetriever docletMessage = getDocletSpecificMsg();
+ if (docletMessage.containsKey(key)) {
+ return docletMessage.getText(key);
}
+ // Check the shared properties file.
+ return message.getText(key);
}
public String getText(String key, String a1) {
- try {
- //Check the doclet specific properties file.
- return getDocletSpecificMsg().getText(key, a1);
- } catch (MissingResourceException e) {
- //Check the shared properties file.
- return message.getText(key, a1);
+ // Check the doclet specific properties file.
+ MessageRetriever docletMessage = getDocletSpecificMsg();
+ if (docletMessage.containsKey(key)) {
+ return docletMessage.getText(key, a1);
}
+ // Check the shared properties file.
+ return message.getText(key, a1);
}
public String getText(String key, String a1, String a2) {
- try {
- //Check the doclet specific properties file.
- return getDocletSpecificMsg().getText(key, a1, a2);
- } catch (MissingResourceException e) {
- //Check the shared properties file.
- return message.getText(key, a1, a2);
+ // Check the doclet specific properties file.
+ MessageRetriever docletMessage = getDocletSpecificMsg();
+ if (docletMessage.containsKey(key)) {
+ return docletMessage.getText(key, a1, a2);
}
+ // Check the shared properties file.
+ return message.getText(key, a1, a2);
}
public String getText(String key, String a1, String a2, String a3) {
- try {
- //Check the doclet specific properties file.
- return getDocletSpecificMsg().getText(key, a1, a2, a3);
- } catch (MissingResourceException e) {
- //Check the shared properties file.
- return message.getText(key, a1, a2, a3);
+ // Check the doclet specific properties file.
+ MessageRetriever docletMessage = getDocletSpecificMsg();
+ if (docletMessage.containsKey(key)) {
+ return docletMessage.getText(key, a1, a2, a3);
}
+ // Check the shared properties file.
+ return message.getText(key, a1, a2, a3);
}
public abstract Content newContent();
diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/MessageRetriever.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/MessageRetriever.java
index 46d9a92123f..63f2ce78cdb 100644
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/MessageRetriever.java
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/MessageRetriever.java
@@ -86,15 +86,32 @@ public class MessageRetriever {
this.resourcelocation = resourcelocation;
}
- private void initRB() {
- if (messageRB == null) {
+ private ResourceBundle initRB() {
+ ResourceBundle bundle = messageRB;
+ if (bundle == null) {
try {
- messageRB = ResourceBundle.getBundle(resourcelocation, configuration.getLocale());
+ messageRB = bundle =
+ ResourceBundle.getBundle(resourcelocation, configuration.getLocale());
} catch (MissingResourceException e) {
throw new Error("Fatal: Resource (" + resourcelocation
+ ") for javadoc doclets is missing.");
}
}
+ return bundle;
+ }
+
+ /**
+ * Determines whether the given key can be retrieved
+ * from this MessageRetriever
+ *
+ * @param key
+ * the resource key
+ * @return true if the given key is
+ * contained in the underlying ResourceBundle.
+ */
+ public boolean containsKey(String key) {
+ ResourceBundle bundle = initRB();
+ return bundle.containsKey(key);
}
/**
@@ -107,8 +124,8 @@ public class MessageRetriever {
* exist in the properties file.
*/
public String getText(String key, Object... args) throws MissingResourceException {
- initRB();
- String message = messageRB.getString(key);
+ ResourceBundle bundle = initRB();
+ String message = bundle.getString(key);
return MessageFormat.format(message, args);
}
diff --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Dependencies.java b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Dependencies.java
index c6fe442e9e3..5de014a08c7 100644
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Dependencies.java
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Dependencies.java
@@ -33,6 +33,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import com.sun.tools.classfile.Dependency.Filter;
@@ -561,13 +562,10 @@ public class Dependencies {
}
static abstract class BasicDependencyFinder implements Finder {
- private Map locations = new HashMap<>();
+ private Map locations = new ConcurrentHashMap<>();
Location getLocation(String className) {
- Location l = locations.get(className);
- if (l == null)
- locations.put(className, l = new SimpleLocation(className));
- return l;
+ return locations.computeIfAbsent(className, cn -> new SimpleLocation(cn));
}
class Visitor implements ConstantPool.Visitor, Type.Visitor {
diff --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java
index 122862252d0..d058badfc9a 100644
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java
@@ -25,23 +25,24 @@
package com.sun.tools.jdeps;
-import java.io.PrintStream;
-import java.util.ArrayList;
+import static com.sun.tools.jdeps.JdepsConfiguration.*;
+
+import com.sun.tools.classfile.Dependency.Location;
+import java.io.IOException;
+import java.io.UncheckedIOException;
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.List;
import java.util.Map;
+import java.util.MissingResourceException;
import java.util.Objects;
+import java.util.Optional;
+import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import com.sun.tools.classfile.Dependency.Location;
-
/**
* Dependency Analyzer.
*/
@@ -52,6 +53,7 @@ public class Analyzer {
*/
public enum Type {
SUMMARY,
+ MODULE, // equivalent to summary in addition, print module descriptor
PACKAGE,
CLASS,
VERBOSE
@@ -62,9 +64,11 @@ public class Analyzer {
* Only the accepted dependencies are recorded.
*/
interface Filter {
- boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive);
+ boolean accepts(Location origin, Archive originArchive,
+ Location target, Archive targetArchive);
}
+ protected final JdepsConfiguration configuration;
protected final Type type;
protected final Filter filter;
protected final Map results = new HashMap<>();
@@ -78,7 +82,8 @@ public class Analyzer {
* @param type Type of the dependency analysis
* @param filter
*/
- public Analyzer(Type type, Filter filter) {
+ Analyzer(JdepsConfiguration config, Type type, Filter filter) {
+ this.configuration = config;
this.type = type;
this.filter = filter;
}
@@ -86,16 +91,10 @@ public class Analyzer {
/**
* Performs the dependency analysis on the given archives.
*/
- public boolean run(Stream extends Archive> archives) {
- return run(archives.collect(Collectors.toList()));
- }
-
- /**
- * Performs the dependency analysis on the given archives.
- */
- public boolean run(Iterable extends Archive> archives) {
- // build a map from Location to Archive
- buildLocationArchiveMap(archives);
+ boolean run(Iterable extends Archive> archives,
+ Map locationMap)
+ {
+ this.locationToArchive.putAll(locationMap);
// traverse and analyze all dependencies
for (Archive archive : archives) {
@@ -106,40 +105,50 @@ public class Analyzer {
return true;
}
- protected void buildLocationArchiveMap(Iterable extends Archive> archives) {
- // build a map from Location to Archive
- for (Archive archive: archives) {
- archive.getClasses()
- .forEach(l -> locationToArchive.putIfAbsent(l, archive));
- }
+ /**
+ * Returns the analyzed archives
+ */
+ Set archives() {
+ return results.keySet();
}
- public boolean hasDependences(Archive archive) {
+ /**
+ * Returns true if the given archive has dependences.
+ */
+ boolean hasDependences(Archive archive) {
if (results.containsKey(archive)) {
return results.get(archive).dependencies().size() > 0;
}
return false;
}
- public Set dependences(Archive source) {
+ /**
+ * Returns the dependences, either class name or package name
+ * as specified in the given verbose level, from the given source.
+ */
+ Set dependences(Archive source) {
if (!results.containsKey(source)) {
return Collections.emptySet();
}
- Dependences result = results.get(source);
- return result.dependencies().stream()
- .map(Dep::target)
- .collect(Collectors.toSet());
+
+ return results.get(source).dependencies()
+ .stream()
+ .map(Dep::target)
+ .collect(Collectors.toSet());
}
- public Stream requires(Archive source) {
+ /**
+ * Returns the direct dependences of the given source
+ */
+ Stream requires(Archive source) {
if (!results.containsKey(source)) {
return Stream.empty();
}
- Dependences result = results.get(source);
- return result.requires().stream().filter(a -> !a.isEmpty());
+ return results.get(source).requires()
+ .stream();
}
- public interface Visitor {
+ interface Visitor {
/**
* Visits a recorded dependency from origin to target which can be
* a fully-qualified classname, a package name, a module or
@@ -153,7 +162,7 @@ public class Analyzer {
* Visit the dependencies of the given source.
* If the requested level is SUMMARY, it will visit the required archives list.
*/
- public void visitDependences(Archive source, Visitor v, Type level) {
+ void visitDependences(Archive source, Visitor v, Type level) {
if (level == Type.SUMMARY) {
final Dependences result = results.get(source);
final Set reqs = result.requires();
@@ -187,7 +196,7 @@ public class Analyzer {
}
}
- public void visitDependences(Archive source, Visitor v) {
+ void visitDependences(Archive source, Visitor v) {
visitDependences(source, v, type);
}
@@ -224,14 +233,28 @@ public class Analyzer {
}
}
+ /*
+ * Returns the archive that contains the given location.
+ */
Archive findArchive(Location t) {
+ // local in this archive
if (archive.getClasses().contains(t))
return archive;
- return locationToArchive.computeIfAbsent(t, _k -> NOT_FOUND);
+ Archive target;
+ if (locationToArchive.containsKey(t)) {
+ target = locationToArchive.get(t);
+ } else {
+ // special case JDK removed API
+ target = configuration.findClass(t)
+ .orElseGet(() -> REMOVED_JDK_INTERNALS.contains(t)
+ ? REMOVED_JDK_INTERNALS
+ : NOT_FOUND);
+ }
+ return locationToArchive.computeIfAbsent(t, _k -> target);
}
- // return classname or package name depedning on the level
+ // return classname or package name depending on the level
private String getLocationName(Location o) {
if (level == Type.CLASS || level == Type.VERBOSE) {
return o.getClassName();
@@ -345,4 +368,66 @@ public class Analyzer {
target, targetArchive.getName());
}
}
+
+ private static final JdkInternals REMOVED_JDK_INTERNALS = new JdkInternals();
+
+ private static class JdkInternals extends Module {
+ private final String BUNDLE = "com.sun.tools.jdeps.resources.jdkinternals";
+
+ private final Set jdkinternals;
+ private final Set jdkUnsupportedClasses;
+ private JdkInternals() {
+ super("JDK removed internal API");
+
+ try {
+ ResourceBundle rb = ResourceBundle.getBundle(BUNDLE);
+ this.jdkinternals = rb.keySet();
+ } catch (MissingResourceException e) {
+ throw new InternalError("Cannot find jdkinternals resource bundle");
+ }
+
+ this.jdkUnsupportedClasses = getUnsupportedClasses();
+ }
+
+ public boolean contains(Location location) {
+ if (jdkUnsupportedClasses.contains(location.getName() + ".class")) {
+ return false;
+ }
+
+ String cn = location.getClassName();
+ int i = cn.lastIndexOf('.');
+ String pn = i > 0 ? cn.substring(0, i) : "";
+ return jdkinternals.contains(cn) || jdkinternals.contains(pn);
+ }
+
+ @Override
+ public String name() {
+ return getName();
+ }
+
+ @Override
+ public boolean isExported(String pn) {
+ return false;
+ }
+
+ private Set getUnsupportedClasses() {
+ // jdk.unsupported may not be observable
+ Optional om = Profile.FULL_JRE.findModule(JDK_UNSUPPORTED);
+ if (om.isPresent()) {
+ return om.get().reader().entries();
+ }
+
+ // find from local run-time image
+ SystemModuleFinder system = new SystemModuleFinder();
+ if (system.find(JDK_UNSUPPORTED).isPresent()) {
+ try {
+ return system.getClassReader(JDK_UNSUPPORTED).entries();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ return Collections.emptySet();
+ }
+ }
}
diff --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Archive.java b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Archive.java
index 90e4890b2b6..cbf25fa4c93 100644
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Archive.java
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Archive.java
@@ -38,6 +38,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Stream;
/**
* Represents the source of the class files.
@@ -86,6 +87,10 @@ public class Archive {
return Module.UNNAMED_MODULE;
}
+ public boolean contains(String entry) {
+ return reader.entries().contains(entry);
+ }
+
public void addClass(Location origin) {
deps.computeIfAbsent(origin, _k -> new HashSet<>());
}
@@ -98,6 +103,15 @@ public class Archive {
return deps.keySet();
}
+ public Stream getDependencies() {
+ return deps.values().stream()
+ .flatMap(Set::stream);
+ }
+
+ public boolean hasDependences() {
+ return getDependencies().count() > 0;
+ }
+
public void visitDependences(Visitor v) {
for (Map.Entry> e: deps.entrySet()) {
for (Location target : e.getValue()) {
diff --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java
index aa2e6ffe7a0..73dd4662f01 100644
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java
@@ -173,7 +173,7 @@ public class ClassFileReader {
static boolean isClass(Path file) {
String fn = file.getFileName().toString();
- return fn.endsWith(".class") && !fn.equals(MODULE_INFO);
+ return fn.endsWith(".class");
}
class FileIterator implements Iterator {
@@ -306,7 +306,7 @@ public class ClassFileReader {
protected Set scan() {
try (JarFile jf = new JarFile(path.toFile())) {
return jf.stream().map(JarEntry::getName)
- .filter(n -> n.endsWith(".class") && !n.endsWith(MODULE_INFO))
+ .filter(n -> n.endsWith(".class"))
.collect(Collectors.toSet());
} catch (IOException e) {
throw new UncheckedIOException(e);
@@ -409,7 +409,7 @@ public class ClassFileReader {
while (entries.hasMoreElements()) {
JarEntry e = entries.nextElement();
String name = e.getName();
- if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
+ if (name.endsWith(".class")) {
return e;
}
}
diff --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java
index 086e13ac1df..fbcb9a0d955 100644
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java
@@ -24,360 +24,285 @@
*/
package com.sun.tools.jdeps;
+import static com.sun.tools.jdeps.Module.*;
+import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
+import static java.util.stream.Collectors.*;
+
import com.sun.tools.classfile.AccessFlags;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.Dependencies;
import com.sun.tools.classfile.Dependency;
+import com.sun.tools.classfile.Dependency.Location;
import java.io.IOException;
-import java.nio.file.Path;
-import java.util.ArrayList;
+import java.io.UncheckedIOException;
import java.util.Deque;
import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.HashSet;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
-import static com.sun.tools.jdeps.Module.*;
-import static com.sun.tools.jdeps.ModulePaths.SystemModulePath.JAVA_BASE;
+/**
+ * Parses class files and finds dependences
+ */
+class DependencyFinder {
+ private static Finder API_FINDER = new Finder(true);
+ private static Finder CLASS_FINDER = new Finder(false);
-public class DependencyFinder {
- private final List roots = new ArrayList<>();
- private final List classpaths = new ArrayList<>();
- private final List modulepaths = new ArrayList<>();
- private final List classes = new ArrayList<>();
- private final boolean compileTimeView;
+ private final JdepsConfiguration configuration;
+ private final JdepsFilter filter;
- DependencyFinder(boolean compileTimeView) {
- this.compileTimeView = compileTimeView;
+ private final Map> parsedArchives = new ConcurrentHashMap<>();
+ private final Map parsedClasses = new ConcurrentHashMap<>();
+
+ private final ExecutorService pool = Executors.newFixedThreadPool(2);
+ private final Deque>> tasks = new ConcurrentLinkedDeque<>();
+
+ DependencyFinder(JdepsConfiguration configuration,
+ JdepsFilter filter) {
+ this.configuration = configuration;
+ this.filter = filter;
+ this.parsedArchives.put(API_FINDER, new ConcurrentLinkedDeque<>());
+ this.parsedArchives.put(CLASS_FINDER, new ConcurrentLinkedDeque<>());
}
- /*
- * Adds a class name to the root set
- */
- void addClassName(String cn) {
- classes.add(cn);
- }
-
- /*
- * Adds the archive of the given path to the root set
- */
- void addRoot(Path path) {
- addRoot(Archive.getInstance(path));
- }
-
- /*
- * Adds the given archive to the root set
- */
- void addRoot(Archive archive) {
- Objects.requireNonNull(archive);
- if (!roots.contains(archive))
- roots.add(archive);
+ Map locationToArchive() {
+ return parsedClasses;
}
/**
- * Add an archive specified in the classpath.
+ * Returns the modules of all dependencies found
*/
- void addClassPathArchive(Path path) {
- addClassPathArchive(Archive.getInstance(path));
+ Stream getDependences(Archive source) {
+ return source.getDependencies()
+ .map(this::locationToArchive)
+ .filter(a -> a != source);
}
/**
- * Add an archive specified in the classpath.
- */
- void addClassPathArchive(Archive archive) {
- Objects.requireNonNull(archive);
- classpaths.add(archive);
- }
-
- /**
- * Add an archive specified in the modulepath.
- */
- void addModule(Module m) {
- Objects.requireNonNull(m);
- modulepaths.add(m);
- }
-
- /**
- * Returns the root set.
- */
- List roots() {
- return roots;
- }
-
- /**
- * Returns a stream of all archives including the root set, module paths,
- * and classpath.
+ * Returns the location to archive map; or NOT_FOUND.
*
- * This only returns the archives with classes parsed.
+ * Location represents a parsed class.
*/
- Stream archives() {
- Stream archives = Stream.concat(roots.stream(), modulepaths.stream());
- archives = Stream.concat(archives, classpaths.stream());
- return archives.filter(a -> !a.isEmpty())
- .distinct();
+ Archive locationToArchive(Location location) {
+ return parsedClasses.containsKey(location)
+ ? parsedClasses.get(location)
+ : configuration.findClass(location).orElse(NOT_FOUND);
}
/**
- * Finds dependencies
- *
- * @param apiOnly API only
- * @param maxDepth depth of transitive dependency analysis; zero indicates
- * @throws IOException
+ * Returns a map from an archive to its required archives
*/
- void findDependencies(JdepsFilter filter, boolean apiOnly, int maxDepth)
- throws IOException
- {
- Dependency.Finder finder =
- apiOnly ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
- : Dependencies.getClassDependencyFinder();
+ Map> dependences() {
+ Map> map = new HashMap<>();
+ parsedArchives.values().stream()
+ .flatMap(Deque::stream)
+ .filter(a -> !a.isEmpty())
+ .forEach(source -> {
+ Set deps = getDependences(source).collect(toSet());
+ if (!deps.isEmpty()) {
+ map.put(source, deps);
+ }
+ });
+ return map;
+ }
- // list of archives to be analyzed
- Set roots = new LinkedHashSet<>(this.roots);
+ boolean isParsed(Location location) {
+ return parsedClasses.containsKey(location);
+ }
- // include java.base in root set
- roots.add(JAVA_BASE);
+ /**
+ * Parses all class files from the given archive stream and returns
+ * all target locations.
+ */
+ public Set parse(Stream extends Archive> archiveStream) {
+ archiveStream.forEach(archive -> parse(archive, CLASS_FINDER));
+ return waitForTasksCompleted();
+ }
- // If -include pattern specified, classes may be in module path or class path.
- // To get compile time view analysis, all classes are analyzed.
- // add all modules except JDK modules to root set
- modulepaths.stream()
- .filter(filter::matches)
- .forEach(roots::add);
+ /**
+ * Parses the exported API class files from the given archive stream and
+ * returns all target locations.
+ */
+ public Set parseExportedAPIs(Stream extends Archive> archiveStream) {
+ archiveStream.forEach(archive -> parse(archive, API_FINDER));
+ return waitForTasksCompleted();
+ }
- // add classpath to the root set
- classpaths.stream()
- .filter(filter::matches)
- .forEach(roots::add);
-
- // transitive dependency
- int depth = maxDepth > 0 ? maxDepth : Integer.MAX_VALUE;
-
- // Work queue of names of classfiles to be searched.
- // Entries will be unique, and for classes that do not yet have
- // dependencies in the results map.
- ConcurrentLinkedDeque deque = new ConcurrentLinkedDeque<>();
- ConcurrentSkipListSet doneClasses = new ConcurrentSkipListSet<>();
-
- TaskExecutor executor = new TaskExecutor(finder, filter, apiOnly, deque, doneClasses);
+ /**
+ * Parses the named class from the given archive and
+ * returns all target locations the named class references.
+ */
+ public Set parse(Archive archive, String name) {
try {
- // get the immediate dependencies of the input files
- for (Archive source : roots) {
- executor.task(source, deque);
- }
- executor.waitForTasksCompleted();
-
- List archives = Stream.concat(Stream.concat(roots.stream(),
- modulepaths.stream()),
- classpaths.stream())
- .collect(Collectors.toList());
-
- // Additional pass to find archive where dependences are identified
- // and also any specified classes, if any.
- // If -R is specified, perform transitive dependency analysis.
- Deque unresolved = new LinkedList<>(classes);
- do {
- String name;
- while ((name = unresolved.poll()) != null) {
- if (doneClasses.contains(name)) {
- continue;
- }
- if (compileTimeView) {
- final String cn = name + ".class";
- // parse all classes in the source archive
- Optional source = archives.stream()
- .filter(a -> a.reader().entries().contains(cn))
- .findFirst();
- trace("%s compile time view %s%n", name, source.map(Archive::getName).orElse(" not found"));
- if (source.isPresent()) {
- executor.runTask(source.get(), deque);
- }
- }
- ClassFile cf = null;
- for (Archive archive : archives) {
- cf = archive.reader().getClassFile(name);
-
- if (cf != null) {
- String classFileName;
- try {
- classFileName = cf.getName();
- } catch (ConstantPoolException e) {
- throw new Dependencies.ClassFileError(e);
- }
- if (!doneClasses.contains(classFileName)) {
- // if name is a fully-qualified class name specified
- // from command-line, this class might already be parsed
- doneClasses.add(classFileName);
- for (Dependency d : finder.findDependencies(cf)) {
- if (depth == 0) {
- // ignore the dependency
- archive.addClass(d.getOrigin());
- break;
- } else if (filter.accepts(d) && filter.accept(archive)) {
- // continue analysis on non-JDK classes
- archive.addClass(d.getOrigin(), d.getTarget());
- String cn = d.getTarget().getName();
- if (!doneClasses.contains(cn) && !deque.contains(cn)) {
- deque.add(cn);
- }
- } else {
- // ensure that the parsed class is added the archive
- archive.addClass(d.getOrigin());
- }
- }
- }
- break;
- }
- }
- if (cf == null) {
- doneClasses.add(name);
- }
- }
- unresolved = deque;
- deque = new ConcurrentLinkedDeque<>();
- } while (!unresolved.isEmpty() && depth-- > 0);
- } finally {
- executor.shutdown();
+ return parse(archive, CLASS_FINDER, name);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
- }
+ }
/**
- * TaskExecutor creates FutureTask to analyze all classes in a given archive
+ * Parses the exported API of the named class from the given archive and
+ * returns all target locations the named class references.
*/
- private class TaskExecutor {
- final ExecutorService pool;
- final Dependency.Finder finder;
- final JdepsFilter filter;
- final boolean apiOnly;
- final Set doneClasses;
- final Map> tasks = new HashMap<>();
-
- TaskExecutor(Dependency.Finder finder,
- JdepsFilter filter,
- boolean apiOnly,
- ConcurrentLinkedDeque deque,
- Set doneClasses) {
- this.pool = Executors.newFixedThreadPool(2);
- this.finder = finder;
- this.filter = filter;
- this.apiOnly = apiOnly;
- this.doneClasses = doneClasses;
+ public Set parseExportedAPIs(Archive archive, String name)
+ {
+ try {
+ return parse(archive, API_FINDER, name);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
+ }
- /**
- * Creates a new task to analyze class files in the given archive.
- * The dependences are added to the given deque for analysis.
- */
- FutureTask task(Archive archive, final ConcurrentLinkedDeque deque) {
- trace("parsing %s %s%n", archive.getName(), archive.path());
- FutureTask task = new FutureTask(new Callable() {
- public Void call() throws Exception {
- for (ClassFile cf : archive.reader().getClassFiles()) {
- String classFileName;
- try {
- classFileName = cf.getName();
- } catch (ConstantPoolException e) {
- throw new Dependencies.ClassFileError(e);
- }
+ private Optional>> parse(Archive archive, Finder finder) {
+ if (parsedArchives.get(finder).contains(archive))
+ return Optional.empty();
- // tests if this class matches the -include
- String cn = classFileName.replace('/', '.');
- if (!filter.matches(cn))
- continue;
+ parsedArchives.get(finder).add(archive);
- // if -apionly is specified, analyze only exported and public types
- if (apiOnly && !(isExported(archive, cn) && cf.access_flags.is(AccessFlags.ACC_PUBLIC)))
- continue;
-
- if (!doneClasses.contains(classFileName)) {
- doneClasses.add(classFileName);
- }
-
- for (Dependency d : finder.findDependencies(cf)) {
- if (filter.accepts(d) && filter.accept(archive)) {
- String name = d.getTarget().getName();
- if (!doneClasses.contains(name) && !deque.contains(name)) {
- deque.add(name);
- }
- archive.addClass(d.getOrigin(), d.getTarget());
- } else {
- // ensure that the parsed class is added the archive
- archive.addClass(d.getOrigin());
- }
- }
+ trace("parsing %s %s%n", archive.getName(), archive.path());
+ FutureTask> task = new FutureTask<>(new Callable<>() {
+ public Set call() throws Exception {
+ Set targets = new HashSet<>();
+ for (ClassFile cf : archive.reader().getClassFiles()) {
+ String classFileName;
+ try {
+ classFileName = cf.getName();
+ } catch (ConstantPoolException e) {
+ throw new Dependencies.ClassFileError(e);
}
- return null;
- }
- });
- tasks.put(archive, task);
- pool.submit(task);
- return task;
- }
- /*
- * This task will parse all class files of the given archive, if it's a new task.
- * This method waits until the task is completed.
- */
- void runTask(Archive archive, final ConcurrentLinkedDeque deque) {
- if (tasks.containsKey(archive))
- return;
-
- FutureTask task = task(archive, deque);
- try {
- // wait for completion
- task.get();
- } catch (InterruptedException|ExecutionException e) {
- throw new Error(e);
- }
- }
-
- /*
- * Waits until all submitted tasks are completed.
- */
- void waitForTasksCompleted() {
- try {
- for (FutureTask t : tasks.values()) {
- if (t.isDone())
+ // filter source class/archive
+ String cn = classFileName.replace('/', '.');
+ if (!finder.accept(archive, cn, cf.access_flags))
continue;
- // wait for completion
- t.get();
+ // tests if this class matches the -include
+ if (!filter.matches(cn))
+ continue;
+
+ for (Dependency d : finder.findDependencies(cf)) {
+ if (filter.accepts(d)) {
+ archive.addClass(d.getOrigin(), d.getTarget());
+ targets.add(d.getTarget());
+ } else {
+ // ensure that the parsed class is added the archive
+ archive.addClass(d.getOrigin());
+ }
+ parsedClasses.putIfAbsent(d.getOrigin(), archive);
+ }
}
- } catch (InterruptedException|ExecutionException e) {
- throw new Error(e);
+
+ return targets;
}
+ });
+ tasks.add(task);
+ pool.submit(task);
+ return Optional.of(task);
+ }
+
+ private Set parse(Archive archive, Finder finder, String name)
+ throws IOException
+ {
+ ClassFile cf = archive.reader().getClassFile(name);
+ if (cf == null) {
+ throw new IllegalArgumentException(archive.getName() + " does not contain " + name);
}
- /*
- * Shutdown the executor service.
- */
- void shutdown() {
- pool.shutdown();
+ Set targets = new HashSet<>();
+ String cn;
+ try {
+ cn = cf.getName().replace('/', '.');
+ } catch (ConstantPoolException e) {
+ throw new Dependencies.ClassFileError(e);
}
- /**
- * Tests if the given class name is exported by the given archive.
- *
- * All packages are exported in unnamed module.
- */
- private boolean isExported(Archive archive, String classname) {
- int i = classname.lastIndexOf('.');
- String pn = i > 0 ? classname.substring(0, i) : "";
- return archive.getModule().isExported(pn);
+ if (!finder.accept(archive, cn, cf.access_flags))
+ return targets;
+
+ // tests if this class matches the -include
+ if (!filter.matches(cn))
+ return targets;
+
+ // skip checking filter.matches
+ for (Dependency d : finder.findDependencies(cf)) {
+ if (filter.accepts(d)) {
+ targets.add(d.getTarget());
+ archive.addClass(d.getOrigin(), d.getTarget());
+ } else {
+ // ensure that the parsed class is added the archive
+ archive.addClass(d.getOrigin());
+ }
+ parsedClasses.putIfAbsent(d.getOrigin(), archive);
+ }
+ return targets;
+ }
+
+ /*
+ * Waits until all submitted tasks are completed.
+ */
+ private Set waitForTasksCompleted() {
+ try {
+ Set targets = new HashSet<>();
+ FutureTask> task;
+ while ((task = tasks.poll()) != null) {
+ // wait for completion
+ if (!task.isDone())
+ targets.addAll(task.get());
+ }
+ return targets;
+ } catch (InterruptedException|ExecutionException e) {
+ throw new Error(e);
+ }
+ }
+
+ /*
+ * Shutdown the executor service.
+ */
+ void shutdown() {
+ pool.shutdown();
+ }
+
+ private interface SourceFilter {
+ boolean accept(Archive archive, String cn, AccessFlags accessFlags);
+ }
+
+ private static class Finder implements Dependency.Finder, SourceFilter {
+ private final Dependency.Finder finder;
+ private final boolean apiOnly;
+ Finder(boolean apiOnly) {
+ this.apiOnly = apiOnly;
+ this.finder = apiOnly
+ ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
+ : Dependencies.getClassDependencyFinder();
+
+ }
+
+ @Override
+ public boolean accept(Archive archive, String cn, AccessFlags accessFlags) {
+ int i = cn.lastIndexOf('.');
+ String pn = i > 0 ? cn.substring(0, i) : "";
+
+ // if -apionly is specified, analyze only exported and public types
+ // All packages are exported in unnamed module.
+ return apiOnly ? archive.getModule().isExported(pn) &&
+ accessFlags.is(AccessFlags.ACC_PUBLIC)
+ : true;
+ }
+
+ @Override
+ public Iterable extends Dependency> findDependencies(ClassFile classfile) {
+ return finder.findDependencies(classfile);
}
}
}
diff --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DepsAnalyzer.java b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DepsAnalyzer.java
new file mode 100644
index 00000000000..376a78a9e1c
--- /dev/null
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DepsAnalyzer.java
@@ -0,0 +1,390 @@
+/*
+ * 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 com.sun.tools.classfile.Dependency.Location;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.sun.tools.jdeps.Analyzer.Type.CLASS;
+import static com.sun.tools.jdeps.Analyzer.Type.VERBOSE;
+import static com.sun.tools.jdeps.Module.trace;
+import static java.util.stream.Collectors.*;
+
+/**
+ * Dependency Analyzer.
+ *
+ * Type of filters:
+ * source filter: -include
+ * target filter: -package, -regex, -requires
+ *
+ * The initial archive set for analysis includes
+ * 1. archives specified in the command line arguments
+ * 2. observable modules matching the source filter
+ * 3. classpath archives matching the source filter or target filter
+ * 4. -addmods and -m root modules
+ */
+public class DepsAnalyzer {
+ final JdepsConfiguration configuration;
+ final JdepsFilter filter;
+ final JdepsWriter writer;
+ final Analyzer.Type verbose;
+ final boolean apiOnly;
+
+ final DependencyFinder finder;
+ final Analyzer analyzer;
+ final List rootArchives = new ArrayList<>();
+
+ // parsed archives
+ final Set archives = new LinkedHashSet<>();
+
+ public DepsAnalyzer(JdepsConfiguration config,
+ JdepsFilter filter,
+ JdepsWriter writer,
+ Analyzer.Type verbose,
+ boolean apiOnly) {
+ this.configuration = config;
+ this.filter = filter;
+ this.writer = writer;
+ this.verbose = verbose;
+ this.apiOnly = apiOnly;
+
+ this.finder = new DependencyFinder(config, filter);
+ this.analyzer = new Analyzer(configuration, verbose, filter);
+
+ // determine initial archives to be analyzed
+ this.rootArchives.addAll(configuration.initialArchives());
+
+ // if -include pattern is specified, add the matching archives on
+ // classpath to the root archives
+ if (filter.hasIncludePattern() || filter.hasTargetFilter()) {
+ configuration.getModules().values().stream()
+ .filter(source -> filter.include(source) && filter.matches(source))
+ .forEach(this.rootArchives::add);
+ }
+
+ // class path archives
+ configuration.classPathArchives().stream()
+ .filter(filter::matches)
+ .forEach(this.rootArchives::add);
+
+ // Include the root modules for analysis
+ this.rootArchives.addAll(configuration.rootModules());
+
+ trace("analyze root archives: %s%n", this.rootArchives);
+ }
+
+ /*
+ * Perform runtime dependency analysis
+ */
+ public boolean run() throws IOException {
+ return run(false, 1);
+ }
+
+ /**
+ * Perform compile-time view or run-time view dependency analysis.
+ *
+ * @param compileTimeView
+ * @param maxDepth depth of recursive analysis. depth == 0 if -R is set
+ */
+ public boolean run(boolean compileTimeView, int maxDepth) throws IOException {
+ try {
+ // parse each packaged module or classpath archive
+ if (apiOnly) {
+ finder.parseExportedAPIs(rootArchives.stream());
+ } else {
+ finder.parse(rootArchives.stream());
+ }
+ archives.addAll(rootArchives);
+
+ int depth = maxDepth > 0 ? maxDepth : Integer.MAX_VALUE;
+
+ // transitive analysis
+ if (depth > 1) {
+ if (compileTimeView)
+ transitiveArchiveDeps(depth-1);
+ else
+ transitiveDeps(depth-1);
+ }
+
+ Set archives = archives();
+
+ // analyze the dependencies collected
+ analyzer.run(archives, finder.locationToArchive());
+
+ writer.generateOutput(archives, analyzer);
+ } finally {
+ finder.shutdown();
+ }
+ return true;
+ }
+
+ /**
+ * Returns the archives for reporting that has matching dependences.
+ *
+ * If -requires is set, they should be excluded.
+ */
+ Set archives() {
+ if (filter.requiresFilter().isEmpty()) {
+ return archives.stream()
+ .filter(filter::include)
+ .filter(Archive::hasDependences)
+ .collect(Collectors.toSet());
+ } else {
+ // use the archives that have dependences and not specified in -requires
+ return archives.stream()
+ .filter(filter::include)
+ .filter(source -> !filter.requiresFilter().contains(source))
+ .filter(source ->
+ source.getDependencies()
+ .map(finder::locationToArchive)
+ .anyMatch(a -> a != source))
+ .collect(Collectors.toSet());
+ }
+ }
+
+ /**
+ * Returns the dependences, either class name or package name
+ * as specified in the given verbose level.
+ */
+ Stream dependences() {
+ return analyzer.archives().stream()
+ .map(analyzer::dependences)
+ .flatMap(Set::stream)
+ .distinct();
+ }
+
+ /**
+ * Returns the archives that contains the given locations and
+ * not parsed and analyzed.
+ */
+ private Set unresolvedArchives(Stream locations) {
+ return locations.filter(l -> !finder.isParsed(l))
+ .distinct()
+ .map(configuration::findClass)
+ .flatMap(Optional::stream)
+ .filter(filter::include)
+ .collect(toSet());
+ }
+
+ /*
+ * Recursively analyzes entire module/archives.
+ */
+ private void transitiveArchiveDeps(int depth) throws IOException {
+ Stream deps = archives.stream()
+ .flatMap(Archive::getDependencies);
+
+ // start with the unresolved archives
+ Set unresolved = unresolvedArchives(deps);
+ do {
+ // parse all unresolved archives
+ Set targets = apiOnly
+ ? finder.parseExportedAPIs(unresolved.stream())
+ : finder.parse(unresolved.stream());
+ archives.addAll(unresolved);
+
+ // Add dependencies to the next batch for analysis
+ unresolved = unresolvedArchives(targets.stream());
+ } while (!unresolved.isEmpty() && depth-- > 0);
+ }
+
+ /*
+ * Recursively analyze the class dependences
+ */
+ private void transitiveDeps(int depth) throws IOException {
+ Stream deps = archives.stream()
+ .flatMap(Archive::getDependencies);
+
+ Deque unresolved = deps.collect(Collectors.toCollection(LinkedList::new));
+ ConcurrentLinkedDeque deque = new ConcurrentLinkedDeque<>();
+ do {
+ Location target;
+ while ((target = unresolved.poll()) != null) {
+ if (finder.isParsed(target))
+ continue;
+
+ Archive archive = configuration.findClass(target).orElse(null);
+ if (archive != null && filter.include(archive)) {
+ archives.add(archive);
+
+ String name = target.getName();
+ Set targets = apiOnly
+ ? finder.parseExportedAPIs(archive, name)
+ : finder.parse(archive, name);
+
+ // build unresolved dependencies
+ targets.stream()
+ .filter(t -> !finder.isParsed(t))
+ .forEach(deque::add);
+ }
+ }
+ unresolved = deque;
+ deque = new ConcurrentLinkedDeque<>();
+ } while (!unresolved.isEmpty() && depth-- > 0);
+ }
+
+ // ----- for testing purpose -----
+
+ public static enum Info {
+ REQUIRES,
+ REQUIRES_PUBLIC,
+ EXPORTED_API,
+ MODULE_PRIVATE,
+ QUALIFIED_EXPORTED_API,
+ INTERNAL_API,
+ JDK_INTERNAL_API
+ }
+
+ public static class Node {
+ public final String name;
+ public final String source;
+ public final Info info;
+ Node(String name, Info info) {
+ this(name, name, info);
+ }
+ Node(String name, String source, Info info) {
+ this.name = name;
+ this.source = source;
+ this.info = info;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (info != Info.REQUIRES && info != Info.REQUIRES_PUBLIC)
+ sb.append(source).append("/");
+
+ sb.append(name);
+ if (info == Info.QUALIFIED_EXPORTED_API)
+ sb.append(" (qualified)");
+ else if (info == Info.JDK_INTERNAL_API)
+ sb.append(" (JDK internal)");
+ else if (info == Info.INTERNAL_API)
+ sb.append(" (internal)");
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Node))
+ return false;
+
+ Node other = (Node)o;
+ return this.name.equals(other.name) &&
+ this.source.equals(other.source) &&
+ this.info.equals(other.info);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name.hashCode();
+ result = 31 * result + source.hashCode();
+ result = 31 * result + info.hashCode();
+ return result;
+ }
+ }
+
+ /**
+ * Returns a graph of module dependences.
+ *
+ * Each Node represents a module and each edge is a dependence.
+ * No analysis on "requires public".
+ */
+ public Graph moduleGraph() {
+ Graph.Builder builder = new Graph.Builder<>();
+
+ archives().stream()
+ .forEach(m -> {
+ Node u = new Node(m.getName(), Info.REQUIRES);
+ builder.addNode(u);
+ analyzer.requires(m)
+ .map(req -> new Node(req.getName(), Info.REQUIRES))
+ .forEach(v -> builder.addEdge(u, v));
+ });
+ return builder.build();
+ }
+
+ /**
+ * Returns a graph of dependences.
+ *
+ * Each Node represents a class or package per the specified verbose level.
+ * Each edge indicates
+ */
+ public Graph dependenceGraph() {
+ Graph.Builder builder = new Graph.Builder<>();
+
+ archives().stream()
+ .map(analyzer.results::get)
+ .filter(deps -> !deps.dependencies().isEmpty())
+ .flatMap(deps -> deps.dependencies().stream())
+ .forEach(d -> addEdge(builder, d));
+ return builder.build();
+ }
+
+ private void addEdge(Graph.Builder builder, Analyzer.Dep dep) {
+ Archive source = dep.originArchive();
+ Archive target = dep.targetArchive();
+ String pn = dep.target();
+ if ((verbose == CLASS || verbose == VERBOSE)) {
+ int i = dep.target().lastIndexOf('.');
+ pn = i > 0 ? dep.target().substring(0, i) : "";
+ }
+ final Info info;
+ if (source == target) {
+ info = Info.MODULE_PRIVATE;
+ } else if (!target.getModule().isNamed()) {
+ info = Info.EXPORTED_API;
+ } else if (target.getModule().isExported(pn)) {
+ info = Info.EXPORTED_API;
+ } else {
+ Module module = target.getModule();
+
+ if (!source.getModule().isJDK() && module.isJDK())
+ info = Info.JDK_INTERNAL_API;
+ // qualified exports or inaccessible
+ else if (module.isExported(pn, source.getModule().name()))
+ info = Info.QUALIFIED_EXPORTED_API;
+ else
+ info = Info.INTERNAL_API;
+ }
+
+ Node u = new Node(dep.origin(), source.getName(), info);
+ Node v = new Node(dep.target(), target.getName(), info);
+ builder.addEdge(u, v);
+ }
+
+}
diff --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Graph.java b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Graph.java
new file mode 100644
index 00000000000..5e9450c5eb5
--- /dev/null
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Graph.java
@@ -0,0 +1,401 @@
+/*
+ * 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.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public final class Graph {
+ private final Set nodes;
+ private final Map> edges;
+
+ public Graph(Set nodes, Map> edges) {
+ this.nodes = Collections.unmodifiableSet(nodes);
+ this.edges = Collections.unmodifiableMap(edges);
+ }
+
+ public Set nodes() {
+ return nodes;
+ }
+
+ public Map> edges() {
+ return edges;
+ }
+
+ public Set adjacentNodes(T u) {
+ return edges.get(u);
+ }
+
+ public boolean contains(T u) {
+ return nodes.contains(u);
+ }
+
+ public Set> edgesFrom(T u) {
+ return edges.get(u).stream()
+ .map(v -> new Edge(u, v))
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Returns a new Graph after transitive reduction
+ */
+ public Graph reduce() {
+ Builder builder = new Builder<>();
+ nodes.stream()
+ .forEach(u -> {
+ builder.addNode(u);
+ edges.get(u).stream()
+ .filter(v -> !pathExists(u, v, false))
+ .forEach(v -> builder.addEdge(u, v));
+ });
+ return builder.build();
+ }
+
+ /**
+ * Returns a new Graph after transitive reduction. All edges in
+ * the given g takes precedence over this graph.
+ *
+ * @throw IllegalArgumentException g must be a subgraph this graph
+ */
+ public Graph reduce(Graph g) {
+ boolean subgraph = nodes.containsAll(g.nodes) &&
+ g.edges.keySet().stream()
+ .allMatch(u -> adjacentNodes(u).containsAll(g.adjacentNodes(u)));
+ if (!subgraph) {
+ throw new IllegalArgumentException(g + " is not a subgraph of " + this);
+ }
+
+ Builder builder = new Builder<>();
+ nodes.stream()
+ .forEach(u -> {
+ builder.addNode(u);
+ // filter the edge if there exists a path from u to v in the given g
+ // or there exists another path from u to v in this graph
+ edges.get(u).stream()
+ .filter(v -> !g.pathExists(u, v) && !pathExists(u, v, false))
+ .forEach(v -> builder.addEdge(u, v));
+ });
+
+ // add the overlapped edges from this graph and the given g
+ g.edges().keySet().stream()
+ .forEach(u -> g.adjacentNodes(u).stream()
+ .filter(v -> isAdjacent(u, v))
+ .forEach(v -> builder.addEdge(u, v)));
+ return builder.build();
+ }
+
+ /**
+ * Returns nodes sorted in topological order.
+ */
+ public Stream orderedNodes() {
+ TopoSorter sorter = new TopoSorter<>(this);
+ return sorter.result.stream();
+ }
+
+ /**
+ * Traverse this graph and performs the given action in topological order
+ */
+ public void ordered(Consumer action) {
+ TopoSorter sorter = new TopoSorter<>(this);
+ sorter.ordered(action);
+ }
+
+ /**
+ * Traverses this graph and performs the given action in reverse topological order
+ */
+ public void reverse(Consumer action) {
+ TopoSorter sorter = new TopoSorter<>(this);
+ sorter.reverse(action);
+ }
+
+ /**
+ * Returns a transposed graph from this graph
+ */
+ public Graph transpose() {
+ Builder builder = new Builder<>();
+ builder.addNodes(nodes);
+ // reverse edges
+ edges.keySet().forEach(u -> {
+ edges.get(u).stream()
+ .forEach(v -> builder.addEdge(v, u));
+ });
+ return builder.build();
+ }
+
+ /**
+ * Returns all nodes reachable from the given set of roots.
+ */
+ public Set dfs(Set roots) {
+ Deque deque = new LinkedList<>(roots);
+ Set visited = new HashSet<>();
+ while (!deque.isEmpty()) {
+ T u = deque.pop();
+ if (!visited.contains(u)) {
+ visited.add(u);
+ if (contains(u)) {
+ adjacentNodes(u).stream()
+ .filter(v -> !visited.contains(v))
+ .forEach(deque::push);
+ }
+ }
+ }
+ return visited;
+ }
+
+ private boolean isAdjacent(T u, T v) {
+ return edges.containsKey(u) && edges.get(u).contains(v);
+ }
+
+ private boolean pathExists(T u, T v) {
+ return pathExists(u, v, true);
+ }
+
+ /**
+ * Returns true if there exists a path from u to v in this graph.
+ * If includeAdjacent is false, it returns true if there exists
+ * another path from u to v of distance > 1
+ */
+ private boolean pathExists(T u, T v, boolean includeAdjacent) {
+ if (!nodes.contains(u) || !nodes.contains(v)) {
+ return false;
+ }
+ if (includeAdjacent && isAdjacent(u, v)) {
+ return true;
+ }
+ Deque stack = new LinkedList<>();
+ Set visited = new HashSet<>();
+ stack.push(u);
+ while (!stack.isEmpty()) {
+ T node = stack.pop();
+ if (node.equals(v)) {
+ return true;
+ }
+ if (!visited.contains(node)) {
+ visited.add(node);
+ edges.get(node).stream()
+ .filter(e -> includeAdjacent || !node.equals(u) || !e.equals(v))
+ .forEach(e -> stack.push(e));
+ }
+ }
+ assert !visited.contains(v);
+ return false;
+ }
+
+ public void printGraph(PrintWriter out) {
+ out.println("graph for " + nodes);
+ nodes.stream()
+ .forEach(u -> adjacentNodes(u).stream()
+ .forEach(v -> out.format(" %s -> %s%n", u, v)));
+ }
+
+ @Override
+ public String toString() {
+ return nodes.toString();
+ }
+
+ static class Edge {
+ final T u;
+ final T v;
+ Edge(T u, T v) {
+ this.u = u;
+ this.v = v;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s -> %s", u, v);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof Edge))
+ return false;
+
+ @SuppressWarnings("unchecked")
+ Edge edge = (Edge) o;
+
+ return u.equals(edge.u) && v.equals(edge.v);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = u.hashCode();
+ result = 31 * result + v.hashCode();
+ return result;
+ }
+ }
+
+ static class Builder {
+ final Set nodes = new HashSet<>();
+ final Map> edges = new HashMap<>();
+
+ public void addNode(T node) {
+ if (nodes.contains(node)) {
+ return;
+ }
+ nodes.add(node);
+ edges.computeIfAbsent(node, _e -> new HashSet<>());
+ }
+
+ public void addNodes(Set nodes) {
+ nodes.addAll(nodes);
+ }
+
+ public void addEdge(T u, T v) {
+ addNode(u);
+ addNode(v);
+ edges.get(u).add(v);
+ }
+
+ public Graph build() {
+ return new Graph(nodes, edges);
+ }
+ }
+
+ /**
+ * Topological sort
+ */
+ static class TopoSorter {
+ final Deque result = new LinkedList<>();
+ final Deque nodes;
+ final Graph graph;
+ TopoSorter(Graph graph) {
+ this.graph = graph;
+ this.nodes = new LinkedList<>(graph.nodes);
+ sort();
+ }
+
+ public void ordered(Consumer action) {
+ result.iterator().forEachRemaining(action);
+ }
+
+ public void reverse(Consumer action) {
+ result.descendingIterator().forEachRemaining(action);
+ }
+
+ private void sort() {
+ Deque visited = new LinkedList<>();
+ Deque done = new LinkedList<>();
+ T node;
+ while ((node = nodes.poll()) != null) {
+ if (!visited.contains(node)) {
+ visit(node, visited, done);
+ }
+ }
+ }
+
+ private void visit(T node, Deque visited, Deque done) {
+ if (visited.contains(node)) {
+ if (!done.contains(node)) {
+ throw new IllegalArgumentException("Cyclic detected: " +
+ node + " " + graph.edges().get(node));
+ }
+ return;
+ }
+ visited.add(node);
+ graph.edges().get(node).stream()
+ .forEach(x -> visit(x, visited, done));
+ done.add(node);
+ result.addLast(node);
+ }
+ }
+
+ public static class DotGraph {
+ static final String ORANGE = "#e76f00";
+ static final String BLUE = "#437291";
+ static final String GRAY = "#dddddd";
+
+ static final String REEXPORTS = "";
+ static final String REQUIRES = "style=\"dashed\"";
+ static final String REQUIRES_BASE = "color=\"" + GRAY + "\"";
+
+ static final Set javaModules = modules(name ->
+ (name.startsWith("java.") && !name.equals("java.smartcardio")));
+ static final Set jdkModules = modules(name ->
+ (name.startsWith("java.") ||
+ name.startsWith("jdk.") ||
+ name.startsWith("javafx.")) && !javaModules.contains(name));
+
+ private static Set modules(Predicate predicate) {
+ return ModuleFinder.ofSystem().findAll()
+ .stream()
+ .map(ModuleReference::descriptor)
+ .map(ModuleDescriptor::name)
+ .filter(predicate)
+ .collect(Collectors.toSet());
+ }
+
+ static void printAttributes(PrintWriter out) {
+ out.format(" size=\"25,25\";%n");
+ out.format(" nodesep=.5;%n");
+ out.format(" ranksep=1.5;%n");
+ out.format(" pencolor=transparent;%n");
+ out.format(" node [shape=plaintext, fontname=\"DejaVuSans\", fontsize=36, margin=\".2,.2\"];%n");
+ out.format(" edge [penwidth=4, color=\"#999999\", arrowhead=open, arrowsize=2];%n");
+ }
+
+ static void printNodes(PrintWriter out, Graph graph) {
+ out.format(" subgraph se {%n");
+ graph.nodes().stream()
+ .filter(javaModules::contains)
+ .forEach(mn -> out.format(" \"%s\" [fontcolor=\"%s\", group=%s];%n",
+ mn, ORANGE, "java"));
+ out.format(" }%n");
+ graph.nodes().stream()
+ .filter(jdkModules::contains)
+ .forEach(mn -> out.format(" \"%s\" [fontcolor=\"%s\", group=%s];%n",
+ mn, BLUE, "jdk"));
+
+ graph.nodes().stream()
+ .filter(mn -> !javaModules.contains(mn) && !jdkModules.contains(mn))
+ .forEach(mn -> out.format(" \"%s\";%n", mn));
+ }
+
+ static void printEdges(PrintWriter out, Graph graph,
+ String node, Set requiresPublic) {
+ graph.adjacentNodes(node).forEach(dn -> {
+ String attr = dn.equals("java.base") ? REQUIRES_BASE
+ : (requiresPublic.contains(dn) ? REEXPORTS : REQUIRES);
+ out.format(" \"%s\" -> \"%s\" [%s];%n", node, dn, attr);
+ });
+ }
+ }
+
+
+}
diff --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/InverseDepsAnalyzer.java b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/InverseDepsAnalyzer.java
new file mode 100644
index 00000000000..7e640d06aaa
--- /dev/null
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/InverseDepsAnalyzer.java
@@ -0,0 +1,250 @@
+/*
+ * 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 static com.sun.tools.jdeps.JdepsFilter.DEFAULT_FILTER;
+import static com.sun.tools.jdeps.Module.trace;
+import static com.sun.tools.jdeps.Graph.*;
+
+import java.lang.module.ModuleDescriptor.Requires;
+import java.io.IOException;
+import java.util.Collections;
+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;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Inverse transitive dependency analysis (compile-time view)
+ */
+public class InverseDepsAnalyzer extends DepsAnalyzer {
+ // the end points for the resulting paths to be reported
+ private final Map> endPoints = new HashMap<>();
+ // target archives for inverse transitive dependence analysis
+ private final Set targets = new HashSet<>();
+
+ public InverseDepsAnalyzer(JdepsConfiguration config,
+ JdepsFilter filter,
+ JdepsWriter writer,
+ Analyzer.Type verbose,
+ boolean apiOnly) {
+ super(config, filter, writer, verbose, apiOnly);
+ }
+
+ public boolean run() throws IOException {
+ try {
+ if (apiOnly) {
+ finder.parseExportedAPIs(rootArchives.stream());
+ } else {
+ finder.parse(rootArchives.stream());
+ }
+ archives.addAll(rootArchives);
+
+ Set archives = archives();
+
+ // If -package or -regex is specified, the archives that reference
+ // the matching types are used as the targets for inverse
+ // transitive analysis. If -requires is specified, the
+ // specified modules are the targets.
+
+ if (filter.requiresFilter().isEmpty()) {
+ targets.addAll(archives);
+ } else {
+ filter.requiresFilter().stream()
+ .map(configuration::findModule)
+ .flatMap(Optional::stream)
+ .forEach(targets::add);
+ }
+
+ // If -package or -regex is specified, the end points are
+ // the matching archives. If -requires is specified,
+ // the end points are the modules specified in -requires.
+ if (filter.requiresFilter().isEmpty()) {
+ Map> dependences = finder.dependences();
+ targets.forEach(source -> endPoints.put(source, dependences.get(source)));
+ } else {
+ targets.forEach(t -> endPoints.put(t, Collections.emptySet()));
+ }
+
+ analyzer.run(archives, finder.locationToArchive());
+
+ // print the first-level of dependencies
+ if (writer != null) {
+ writer.generateOutput(archives, analyzer);
+ }
+
+ } finally {
+ finder.shutdown();
+ }
+ return true;
+ }
+
+ /**
+ * Returns the target archives determined from the dependency analysis.
+ *
+ * Inverse transitive dependency will find all nodes that depend
+ * upon the returned set of archives directly and indirectly.
+ */
+ public Set targets() {
+ return Collections.unmodifiableSet(targets);
+ }
+
+ /**
+ * Finds all inverse transitive dependencies using the given requires set
+ * as the targets, if non-empty. If the given requires set is empty,
+ * use the archives depending the packages specified in -regex or -p options.
+ */
+ public Set> inverseDependences() throws IOException {
+ // create a new dependency finder to do the analysis
+ DependencyFinder dependencyFinder = new DependencyFinder(configuration, DEFAULT_FILTER);
+ try {
+ // parse all archives in unnamed module to get compile-time dependences
+ Stream archives =
+ Stream.concat(configuration.initialArchives().stream(),
+ configuration.classPathArchives().stream());
+ if (apiOnly) {
+ dependencyFinder.parseExportedAPIs(archives);
+ } else {
+ dependencyFinder.parse(archives);
+ }
+
+ Graph.Builder builder = new Graph.Builder<>();
+ // include all target nodes
+ targets().forEach(builder::addNode);
+
+ // transpose the module graph - may filter JDK module
+ configuration.getModules().values().stream()
+ .filter(filter::include)
+ .forEach(m -> {
+ builder.addNode(m);
+ m.descriptor().requires().stream()
+ .map(Requires::name)
+ .map(configuration::findModule) // must be present
+ .forEach(v -> builder.addEdge(v.get(), m));
+ });
+
+ // add the dependences from the analysis
+ Map> dependences = dependencyFinder.dependences();
+ dependences.entrySet().stream()
+ .forEach(e -> {
+ Archive u = e.getKey();
+ builder.addNode(u);
+ e.getValue().forEach(v -> builder.addEdge(v, u));
+ });
+
+ // transposed dependence graph.
+ Graph graph = builder.build();
+ trace("targets: %s%n", targets());
+
+ // Traverse from the targets and find all paths
+ // rebuild a graph with all nodes that depends on targets
+ // targets directly and indirectly
+ return targets().stream()
+ .map(t -> findPaths(graph, t))
+ .flatMap(Set::stream)
+ .collect(Collectors.toSet());
+ } finally {
+ dependencyFinder.shutdown();
+ }
+ }
+
+ /**
+ * Returns all paths reachable from the given targets.
+ */
+ private Set> findPaths(Graph graph, Archive target) {
+
+ // path is in reversed order
+ Deque path = new LinkedList<>();
+ path.push(target);
+
+ Set> visited = new HashSet<>();
+
+ Deque> deque = new LinkedList<>();
+ deque.addAll(graph.edgesFrom(target));
+ if (deque.isEmpty()) {
+ return makePaths(path).collect(Collectors.toSet());
+ }
+
+ Set> allPaths = new HashSet<>();
+ while (!deque.isEmpty()) {
+ Edge edge = deque.pop();
+
+ if (visited.contains(edge))
+ continue;
+
+ Archive node = edge.v;
+ path.addLast(node);
+ visited.add(edge);
+
+ Set> unvisitedDeps = graph.edgesFrom(node)
+ .stream()
+ .filter(e -> !visited.contains(e))
+ .collect(Collectors.toSet());
+
+ trace("visiting %s %s (%s)%n", edge, path, unvisitedDeps);
+ if (unvisitedDeps.isEmpty()) {
+ makePaths(path).forEach(allPaths::add);
+ path.removeLast();
+ }
+
+ // push unvisited adjacent edges
+ unvisitedDeps.stream().forEach(deque::push);
+
+
+ // when the adjacent edges of a node are visited, pop it from the path
+ while (!path.isEmpty()) {
+ if (visited.containsAll(graph.edgesFrom(path.peekLast())))
+ path.removeLast();
+ else
+ break;
+ }
+ }
+
+ return allPaths;
+ }
+
+ /**
+ * Prepend end point to the path
+ */
+ private Stream> makePaths(Deque path) {
+ Set nodes = endPoints.get(path.peekFirst());
+ if (nodes == null || nodes.isEmpty()) {
+ return Stream.of(new LinkedList<>(path));
+ } else {
+ return nodes.stream().map(n -> {
+ Deque newPath = new LinkedList<>();
+ newPath.addFirst(n);
+ newPath.addAll(path);
+ return newPath;
+ });
+ }
+ }
+}
diff --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java
new file mode 100644
index 00000000000..26c5967f36c
--- /dev/null
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright (c) 2012, 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 static com.sun.tools.jdeps.Module.trace;
+import static java.util.stream.Collectors.*;
+
+import com.sun.tools.classfile.Dependency;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.lang.module.ResolvedModule;
+import java.net.URI;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+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.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+public class JdepsConfiguration {
+ // the token for "all modules on the module path"
+ public static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
+ public static final String ALL_DEFAULT = "ALL-DEFAULT";
+ public static final String MODULE_INFO = "module-info.class";
+
+ private final SystemModuleFinder system;
+ private final ModuleFinder finder;
+
+ private final Map nameToModule = new LinkedHashMap<>();
+ private final Map packageToModule = new HashMap<>();
+ private final Map> packageToUnnamedModule = new HashMap<>();
+
+ private final List classpathArchives = new ArrayList<>();
+ private final List initialArchives = new ArrayList<>();
+ private final Set rootModules = new HashSet<>();
+ private final Configuration configuration;
+
+ private JdepsConfiguration(SystemModuleFinder systemModulePath,
+ ModuleFinder finder,
+ Set roots,
+ List classpaths,
+ List initialArchives,
+ boolean allDefaultModules)
+ throws IOException
+ {
+ trace("root: %s%n", roots);
+
+ this.system = systemModulePath;
+ this.finder = finder;
+
+ // build root set for resolution
+ Set mods = new HashSet<>(roots);
+
+ // add default modules to the root set
+ // unnamed module
+ if (!initialArchives.isEmpty() || !classpaths.isEmpty() ||
+ roots.isEmpty() || allDefaultModules) {
+ mods.addAll(systemModulePath.defaultSystemRoots());
+ }
+
+ this.configuration = Configuration.empty()
+ .resolveRequires(finder, ModuleFinder.of(), mods);
+
+ this.configuration.modules().stream()
+ .map(ResolvedModule::reference)
+ .forEach(this::addModuleReference);
+
+ // packages in unnamed module
+ initialArchives.forEach(archive -> {
+ addPackagesInUnnamedModule(archive);
+ this.initialArchives.add(archive);
+ });
+
+ // classpath archives
+ for (Path p : classpaths) {
+ if (Files.exists(p)) {
+ Archive archive = Archive.getInstance(p);
+ addPackagesInUnnamedModule(archive);
+ classpathArchives.add(archive);
+ }
+ }
+
+ // all roots specified in -addmods or -m are included
+ // as the initial set for analysis.
+ roots.stream()
+ .map(nameToModule::get)
+ .forEach(this.rootModules::add);
+
+ initProfiles();
+
+ trace("resolved modules: %s%n", nameToModule.keySet().stream()
+ .sorted().collect(joining("\n", "\n", "")));
+ }
+
+ private void initProfiles() {
+ // other system modules are not observed and not added in nameToModule map
+ Map systemModules =
+ system.moduleNames()
+ .collect(toMap(Function.identity(), (mn) -> {
+ Module m = nameToModule.get(mn);
+ if (m == null) {
+ ModuleReference mref = finder.find(mn).get();
+ m = toModule(mref);
+ }
+ return m;
+ }));
+ Profile.init(systemModules);
+ }
+
+ private void addModuleReference(ModuleReference mref) {
+ Module module = toModule(mref);
+ nameToModule.put(mref.descriptor().name(), module);
+ mref.descriptor().packages()
+ .forEach(pn -> packageToModule.putIfAbsent(pn, module));
+ }
+
+ private void addPackagesInUnnamedModule(Archive archive) {
+ archive.reader().entries().stream()
+ .filter(e -> e.endsWith(".class") && !e.equals(MODULE_INFO))
+ .map(this::toPackageName)
+ .distinct()
+ .forEach(pn -> packageToUnnamedModule
+ .computeIfAbsent(pn, _n -> new ArrayList<>()).add(archive));
+ }
+
+ private String toPackageName(String name) {
+ int i = name.lastIndexOf('/');
+ return i > 0 ? name.replace('/', '.').substring(0, i) : "";
+ }
+
+ public Optional findModule(String name) {
+ Objects.requireNonNull(name);
+ Module m = nameToModule.get(name);
+ return m!= null ? Optional.of(m) : Optional.empty();
+
+ }
+
+ public Optional findModuleDescriptor(String name) {
+ Objects.requireNonNull(name);
+ Module m = nameToModule.get(name);
+ return m!= null ? Optional.of(m.descriptor()) : Optional.empty();
+ }
+
+ boolean isSystem(Module m) {
+ return system.find(m.name()).isPresent();
+ }
+
+ boolean isValidToken(String name) {
+ return ALL_MODULE_PATH.equals(name) || ALL_DEFAULT.equals(name);
+ }
+
+ /**
+ * Returns the modules that the given module can read
+ */
+ public Stream reads(Module module) {
+ return configuration.findModule(module.name()).get()
+ .reads().stream()
+ .map(ResolvedModule::name)
+ .map(nameToModule::get);
+ }
+
+ /**
+ * Returns the list of packages that split between resolved module and
+ * unnamed module
+ */
+ public Map> splitPackages() {
+ Set splitPkgs = packageToModule.keySet().stream()
+ .filter(packageToUnnamedModule::containsKey)
+ .collect(toSet());
+ if (splitPkgs.isEmpty())
+ return Collections.emptyMap();
+
+ return splitPkgs.stream().collect(toMap(Function.identity(), (pn) -> {
+ Set sources = new LinkedHashSet<>();
+ sources.add(packageToModule.get(pn).getModule().location().toString());
+ packageToUnnamedModule.get(pn).stream()
+ .map(Archive::getPathName)
+ .forEach(sources::add);
+ return sources;
+ }));
+ }
+
+ /**
+ * Returns an optional archive containing the given Location
+ */
+ public Optional findClass(Dependency.Location location) {
+ String name = location.getName();
+ int i = name.lastIndexOf('/');
+ String pn = i > 0 ? name.substring(0, i).replace('/', '.') : "";
+ Archive archive = packageToModule.get(pn);
+ if (archive != null) {
+ return archive.contains(name + ".class")
+ ? Optional.of(archive)
+ : Optional.empty();
+ }
+
+ if (packageToUnnamedModule.containsKey(pn)) {
+ return packageToUnnamedModule.get(pn).stream()
+ .filter(a -> a.contains(name + ".class"))
+ .findFirst();
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Returns the list of Modules that can be found in the specified
+ * module paths.
+ */
+ public Map getModules() {
+ return nameToModule;
+ }
+
+ public Stream resolve(Set roots) {
+ if (roots.isEmpty()) {
+ return nameToModule.values().stream();
+ } else {
+ return Configuration.empty()
+ .resolveRequires(finder, ModuleFinder.of(), roots)
+ .modules().stream()
+ .map(ResolvedModule::name)
+ .map(nameToModule::get);
+ }
+ }
+
+ public List classPathArchives() {
+ return classpathArchives;
+ }
+
+ public List initialArchives() {
+ return initialArchives;
+ }
+
+ public Set rootModules() {
+ return rootModules;
+ }
+
+ public Module toModule(ModuleReference mref) {
+ try {
+ String mn = mref.descriptor().name();
+ URI location = mref.location().orElseThrow(FileNotFoundException::new);
+ ModuleDescriptor md = mref.descriptor();
+ Module.Builder builder = new Module.Builder(md, system.find(mn).isPresent());
+
+ final ClassFileReader reader;
+ if (location.getScheme().equals("jrt")) {
+ reader = system.getClassReader(mn);
+ } else {
+ reader = ClassFileReader.newInstance(Paths.get(location));
+ }
+
+ builder.classes(reader);
+ builder.location(location);
+
+ return builder.build();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ static class SystemModuleFinder implements ModuleFinder {
+ private static final String JAVA_HOME = System.getProperty("java.home");
+ private static final String JAVA_SE = "java.se";
+
+ private final FileSystem fileSystem;
+ private final Path root;
+ private final Map systemModules;
+
+ SystemModuleFinder() {
+ if (Files.isRegularFile(Paths.get(JAVA_HOME, "lib", "modules"))) {
+ // jrt file system
+ this.fileSystem = FileSystems.getFileSystem(URI.create("jrt:/"));
+ this.root = fileSystem.getPath("/modules");
+ this.systemModules = walk(root);
+ } else {
+ // exploded image
+ this.fileSystem = FileSystems.getDefault();
+ root = Paths.get(JAVA_HOME, "modules");
+ this.systemModules = ModuleFinder.ofSystem().findAll().stream()
+ .collect(toMap(mref -> mref.descriptor().name(), Function.identity()));
+ }
+ }
+
+ SystemModuleFinder(String javaHome) throws IOException {
+ if (javaHome == null) {
+ // -system none
+ this.fileSystem = null;
+ this.root = null;
+ this.systemModules = Collections.emptyMap();
+ } else {
+ if (Files.isRegularFile(Paths.get(javaHome, "lib", "modules")))
+ throw new IllegalArgumentException("Invalid java.home: " + javaHome);
+
+ // alternate java.home
+ Map env = new HashMap<>();
+ env.put("java.home", javaHome);
+ // a remote run-time image
+ this.fileSystem = FileSystems.newFileSystem(URI.create("jrt:/"), env);
+ this.root = fileSystem.getPath("/modules");
+ this.systemModules = walk(root);
+ }
+ }
+
+ private Map walk(Path root) {
+ try {
+ return Files.walk(root, 1)
+ .filter(path -> !path.equals(root))
+ .map(this::toModuleReference)
+ .collect(toMap(mref -> mref.descriptor().name(),
+ Function.identity()));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private ModuleReference toModuleReference(Path path) {
+ Path minfo = path.resolve(MODULE_INFO);
+ try (InputStream in = Files.newInputStream(minfo);
+ BufferedInputStream bin = new BufferedInputStream(in)) {
+
+ ModuleDescriptor descriptor = dropHashes(ModuleDescriptor.read(bin));
+ String mn = descriptor.name();
+ URI uri = URI.create("jrt:/" + path.getFileName().toString());
+ Supplier readerSupplier = new Supplier<>() {
+ @Override
+ public ModuleReader get() {
+ return new ModuleReader() {
+ @Override
+ public Optional find(String name) throws IOException {
+ return name.equals(mn)
+ ? Optional.of(uri) : Optional.empty();
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ };
+ }
+ };
+
+ return new ModuleReference(descriptor, uri, readerSupplier);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private ModuleDescriptor dropHashes(ModuleDescriptor md) {
+ ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(md.name());
+ md.requires().forEach(builder::requires);
+ md.exports().forEach(builder::exports);
+ md.provides().values().stream().forEach(builder::provides);
+ md.uses().stream().forEach(builder::uses);
+ builder.conceals(md.conceals());
+ return builder.build();
+ }
+
+ @Override
+ public Set findAll() {
+ return systemModules.values().stream().collect(toSet());
+ }
+
+ @Override
+ public Optional find(String mn) {
+ return systemModules.containsKey(mn)
+ ? Optional.of(systemModules.get(mn)) : Optional.empty();
+ }
+
+ public Stream moduleNames() {
+ return systemModules.values().stream()
+ .map(mref -> mref.descriptor().name());
+ }
+
+ public ClassFileReader getClassReader(String modulename) throws IOException {
+ Path mp = root.resolve(modulename);
+ if (Files.exists(mp) && Files.isDirectory(mp)) {
+ return ClassFileReader.newInstance(fileSystem, mp);
+ } else {
+ throw new FileNotFoundException(mp.toString());
+ }
+ }
+
+ public Set defaultSystemRoots() {
+ Set roots = new HashSet<>();
+ boolean hasJava = false;
+ if (systemModules.containsKey(JAVA_SE)) {
+ // java.se is a system module
+ hasJava = true;
+ roots.add(JAVA_SE);
+ }
+
+ for (ModuleReference mref : systemModules.values()) {
+ String mn = mref.descriptor().name();
+ if (hasJava && mn.startsWith("java."))
+ continue;
+
+ // add as root if observable and exports at least one package
+ ModuleDescriptor descriptor = mref.descriptor();
+ for (ModuleDescriptor.Exports e : descriptor.exports()) {
+ if (!e.isQualified()) {
+ roots.add(mn);
+ break;
+ }
+ }
+ }
+ return roots;
+ }
+ }
+
+ public static class Builder {
+
+ final SystemModuleFinder systemModulePath;
+ final Set rootModules = new HashSet<>();
+ final List initialArchives = new ArrayList<>();
+ final List paths = new ArrayList<>();
+ final List classPaths = new ArrayList<>();
+
+ ModuleFinder upgradeModulePath;
+ ModuleFinder appModulePath;
+ boolean addAllApplicationModules;
+ boolean addAllDefaultModules;
+
+ public Builder() {
+ this.systemModulePath = new SystemModuleFinder();
+ }
+
+ public Builder(String javaHome) throws IOException {
+ this.systemModulePath = SystemModuleFinder.JAVA_HOME.equals(javaHome)
+ ? new SystemModuleFinder()
+ : new SystemModuleFinder(javaHome);
+ }
+
+ public Builder upgradeModulePath(String upgradeModulePath) {
+ this.upgradeModulePath = createModulePathFinder(upgradeModulePath);
+ return this;
+ }
+
+ public Builder appModulePath(String modulePath) {
+ this.appModulePath = createModulePathFinder(modulePath);
+ return this;
+ }
+
+ public Builder addmods(Set addmods) {
+ for (String mn : addmods) {
+ switch (mn) {
+ case ALL_MODULE_PATH:
+ this.addAllApplicationModules = true;
+ break;
+ case ALL_DEFAULT:
+ this.addAllDefaultModules = true;
+ break;
+ default:
+ this.rootModules.add(mn);
+ }
+ }
+ return this;
+ }
+
+ /*
+ * This method is for -check option to find all target modules specified
+ * in qualified exports.
+ *
+ * Include all system modules and modules found on modulepath
+ */
+ public Builder allModules() {
+ systemModulePath.moduleNames()
+ .forEach(this.rootModules::add);
+ this.addAllApplicationModules = true;
+ return this;
+ }
+
+ public Builder addRoot(Path path) {
+ Archive archive = Archive.getInstance(path);
+ if (archive.contains(MODULE_INFO)) {
+ paths.add(path);
+ } else {
+ initialArchives.add(archive);
+ }
+ return this;
+ }
+
+ public Builder addClassPath(String classPath) {
+ this.classPaths.addAll(getClassPaths(classPath));
+ return this;
+ }
+
+ public JdepsConfiguration build() throws IOException {
+ ModuleFinder finder = systemModulePath;
+ if (upgradeModulePath != null) {
+ finder = ModuleFinder.compose(upgradeModulePath, systemModulePath);
+ }
+ if (appModulePath != null) {
+ finder = ModuleFinder.compose(finder, appModulePath);
+ }
+ if (!paths.isEmpty()) {
+ ModuleFinder otherModulePath = ModuleFinder.of(paths.toArray(new Path[0]));
+
+ finder = ModuleFinder.compose(finder, otherModulePath);
+ // add modules specified on command-line (convenience) as root set
+ otherModulePath.findAll().stream()
+ .map(mref -> mref.descriptor().name())
+ .forEach(rootModules::add);
+ }
+ if (addAllApplicationModules && appModulePath != null) {
+ appModulePath.findAll().stream()
+ .map(mref -> mref.descriptor().name())
+ .forEach(rootModules::add);
+ }
+
+ return new JdepsConfiguration(systemModulePath,
+ finder,
+ rootModules,
+ classPaths,
+ initialArchives,
+ addAllDefaultModules);
+ }
+
+ private static ModuleFinder createModulePathFinder(String mpaths) {
+ if (mpaths == null) {
+ return null;
+ } else {
+ String[] dirs = mpaths.split(File.pathSeparator);
+ Path[] paths = new Path[dirs.length];
+ int i = 0;
+ for (String dir : dirs) {
+ paths[i++] = Paths.get(dir);
+ }
+ return ModuleFinder.of(paths);
+ }
+ }
+
+ /*
+ * Returns the list of Archive specified in cpaths and not included
+ * initialArchives
+ */
+ private List getClassPaths(String cpaths) {
+ if (cpaths.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List paths = new ArrayList<>();
+ for (String p : cpaths.split(File.pathSeparator)) {
+ if (p.length() > 0) {
+ // wildcard to parse all JAR files e.g. -classpath dir/*
+ int i = p.lastIndexOf(".*");
+ if (i > 0) {
+ Path dir = Paths.get(p.substring(0, i));
+ try (DirectoryStream stream = Files.newDirectoryStream(dir, "*.jar")) {
+ for (Path entry : stream) {
+ paths.add(entry);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ } else {
+ paths.add(Paths.get(p));
+ }
+ }
+ }
+ return paths;
+ }
+ }
+
+}
diff --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsFilter.java b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsFilter.java
index 84b04e2e713..585ec524724 100644
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsFilter.java
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsFilter.java
@@ -41,22 +41,27 @@ 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. -module specifies to match target dependencies from the given module
+ * 3. -requires 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
* containing archive of a target class may not be known until
* the entire archive
*/
-class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
+public class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
+
+ public static final JdepsFilter DEFAULT_FILTER =
+ new JdepsFilter.Builder().filter(true, true).build();
+
private final Dependency.Filter filter;
private final Pattern filterPattern;
private final boolean filterSamePackage;
private final boolean filterSameArchive;
private final boolean findJDKInternals;
private final Pattern includePattern;
- private final Set includePackages;
- private final Set excludeModules;
+ private final Pattern includeSystemModules;
+
+ private final Set