diff --git a/code.js b/code.js
index ebba234..c3eb126 100644
--- a/code.js
+++ b/code.js
@@ -5,18 +5,18 @@ var broken = null // array of 2-arrays of int, the node ids of broken edges.
function onLoad() {
// Grab data from server: package graph, "directory" tree, broken edges.
- var data = null
- jQuery.ajax({
- url: "/data",
- async: false,
- success: function(json) {data = json},
- })
+ // TODO(adonovan): opt: put JSON data in the /index.html page?
+ // There's no need for a second HTTP request.
+ jQuery.ajax({url: "/data", success: onData})
+}
+// onData is called shortly after page load with the result of the /data request.
+function onData(data) {
// Save array of Package objects.
packages = data.Packages
- // Show initial (root) packages.
- $('#roots').text(data.Roots.map(i => packages[i].PkgPath).join("\n"))
+ // Show initial packages.
+ $('#initial').text(data.Initial.map(i => packages[i].PkgPath).join("\n"))
// Show broken edges.
broken = data.Broken
@@ -58,65 +58,59 @@ function onLoad() {
}
})
- // Search the tree when the user types in the search box.
- $("#search").keyup(function () {
- var searchString = $(this).val();
- $('#tree').jstree('search', searchString);
- });
-
// Show package info when a node is clicked.
$('#tree').on("changed.jstree", function (e, data) {
if (data.node) {
selectPkg(data.node.original)
}
})
+
+ // Search the tree when the user types in the search box.
+ $("#search").keyup(function () {
+ var searchString = $(this).val();
+ $('#tree').jstree('search', searchString);
+ });
}
// selectPkg shows package info (if any) about the clicked node.
function selectPkg(json) {
- if (json.Package == null) {
- // Non-package "directory" node: grey out the fields.
+ if (json.Package < 0) {
+ // Non-package "directory" node: clear the fields.
$('#json').text("")
- $('#pkgname').text("N/A")
+ $('#pkgname').text("none")
$('#doc').text("")
$('#imports').text("")
- $('#dom').text("")
$('#path').text("")
return
}
// A package node was selected.
- var pkg = packages[json.Package]
+ var pkg = packages[json.Package]
+
+ // Show JSON, for debugging.
$('#json').html("" + JSON.stringify(json) + "
")
+
+ // Show selected package.
$('#pkgname').text(pkg.PkgPath)
+
+ // Set link to package documentation.
$('#doc').html("doc")
// TODO(adonovan): display imports as a set of links,
// with as ImportPath text and "select dir tree node" as action.
- if (json.Imports != null) { // TODO(adonovan): how can this be null?
- $('#imports').text(json.Imports.join(" "))
- }
-
- // Show dominator tree.
- var html = ""
- var doms = [].concat(json.Dominators).reverse()
- for (var i in doms) {
- html += (i > 0 ? " ⟶ " : "") + "" + doms[i] + "
"
- }
- $('#dom').html(html)
+ $('#imports').text(json.Imports.join(" "))
// Show "break edges" buttons.
var html = ""
var path = [].concat(json.Path).reverse() // from root to selected package
for (var i in path) {
var p = packages[path[i]]
- if (i == 0) { // root
- html += "" + p.PkgPath + "
"
- } else {
+ if (i >= 0) {
html += " "
+ " "
- + "⟶ " + p.PkgPath + "
"
+ + "⟶ "
}
+ html += "" + p.PkgPath + "
"
}
$('#path').html(html)
}
diff --git a/dom.go b/dom.go
index 517f554..b5d56c2 100644
--- a/dom.go
+++ b/dom.go
@@ -51,8 +51,7 @@ package main
// to avoid the need for buckets of size > 1.
// Idom returns the block that immediately dominates b:
-// its parent in the dominator tree, if any.
-// Root nodes have no parent.
+// its parent in the dominator tree, if any. The root node has no parent.
func (b *node) Idom() *node { return b.dom.idom }
// Dominees returns the list of blocks that b immediately dominates:
@@ -99,7 +98,6 @@ func (lt *ltState) dfs(v *node, i int32, preorder []*node) int32 {
// eval implements the EVAL part of the LT algorithm.
func (lt *ltState) eval(v *node) *node {
-
u := v
for ; lt.ancestor[v.dom.index] != nil; v = lt.ancestor[v.dom.index] {
if lt.sdom[v.dom.index].dom.pre < lt.sdom[u.dom.index].dom.pre {
@@ -114,8 +112,8 @@ func (lt *ltState) link(v, w *node) {
lt.ancestor[w.dom.index] = v
}
-// buildDomTree computes the dominator tree of f using the LT algorithm,
-// starting from the roots indicated by node.isroot.
+// buildDomTree computes the dominator tree of f using the LT algorithm.
+// The first node is the distinguished root node.
func buildDomTree(nodes []*node) {
// The step numbers refer to the original LT paper; the
// reordering is due to Georgiadis.
@@ -125,12 +123,15 @@ func buildDomTree(nodes []*node) {
b.dom = domInfo{index: -1}
}
+ root := nodes[0]
+
// The original (ssa) implementation had the precondition
// that all nodes are reachable, but because of Spaghetti's
// "broken edges", some nodes may be unreachable.
// We filter them out now with another graph traversal.
// The domInfo.index numbering is relative to this ordering.
// See other "reachable hack" comments for related parts.
+ // We should combine this into step 1.
var reachable []*node
var visit func(n *node)
visit = func(n *node) {
@@ -142,11 +143,7 @@ func buildDomTree(nodes []*node) {
}
}
}
- for _, n := range nodes {
- if n.isroot {
- visit(n)
- }
- }
+ visit(root)
nodes = reachable
n := len(nodes)
@@ -161,12 +158,7 @@ func buildDomTree(nodes []*node) {
// Step 1. Number vertices by depth-first preorder.
preorder := space[3*n : 4*n]
- var prenum int32
- for _, w := range nodes {
- if w.isroot {
- prenum = lt.dfs(w, prenum, preorder)
- }
- }
+ lt.dfs(root, 0, preorder)
buckets := space[4*n : 5*n]
copy(buckets, preorder)
@@ -215,7 +207,7 @@ func buildDomTree(nodes []*node) {
// Step 4. Explicitly define the immediate dominator of each
// node, in preorder.
for _, w := range preorder[1:] {
- if w.isroot {
+ if w == root {
w.dom.idom = nil
} else {
if w.dom.idom != lt.sdom[w.dom.index] {
@@ -226,12 +218,8 @@ func buildDomTree(nodes []*node) {
}
}
- var pre, post int32
- for _, w := range nodes {
- if w.isroot {
- pre, post = numberDomTree(w, pre, post)
- }
- }
+ // Number all nodes to enable O(1) dominance queries.
+ numberDomTree(root, 0, 0)
}
// numberDomTree sets the pre- and post-order numbers of a depth-first
diff --git a/index.html b/index.html
index aca2584..b9271e8 100644
--- a/index.html
+++ b/index.html
@@ -12,26 +12,29 @@
- This tool displays the complete dependencies of these root packages: + This tool displays the complete dependencies of these initial packages:
-...+
...
Click on a package in the tree view to display information about it, including a path by which it is reached from one of the - root packages. Use the break button to remove an + initial packages. Use the break button to remove an edge from the graph, so that you can assess what edges need to be broken to remove an unwanted dependency.
- ⓘ This tree shows
- all dependencies of the root packages, grouped hierarchically
- by import path and containing module.
Each package has a
- numeric weight, computed using network flow: this is the size
- of the graph rooted at the node, divided by the node's
- in-degree. Click a package to show more information about
- it.
+ ⓘ
+ This tree shows all dependencies of the initial packages,
+ grouped hierarchically by import path and containing
+ module.
+
+ Each package has a numeric weight, computed using network
+ flow: this is the size of the graph rooted at the node,
+ divided by the node's in-degree.
+
+ Click a package to show more information aboutit.