collapse method
Compute a collapsed version of the call-graph, where
Implementation
CallGraph collapse(NodeType type, {bool dropCallNodes = false}) {
final graphNodesByData = <Object, CallGraphNode>{};
final graphNodeByEntityId = <CallGraphNode?>[];
ProgramInfoNode collapsed(ProgramInfoNode nn) {
// Root always collapses onto itself.
if (nn == program.root) {
return nn;
}
// Even though all code is grouped into libraries, not all libraries
// are grouped into packages (e.g. dart:* libraries). Meaning
// that if we are collapsing by package we need to stop right before
// hitting the root node.
var n = nn;
while (n.parent != program.root && n.type != type) {
n = n.parent!;
}
return n;
}
CallGraphNode callGraphNodeFor(Object data) {
return graphNodesByData.putIfAbsent(data, () {
final n = CallGraphNode(graphNodesByData.length, data: data);
if (data is ProgramInfoNode) {
if (graphNodeByEntityId.length <= data.id) {
graphNodeByEntityId.length = data.id * 2 + 1;
}
graphNodeByEntityId[data.id] = n;
}
return n;
});
}
final newNodes = nodes.map((n) {
if (n.data is ProgramInfoNode) {
return callGraphNodeFor(collapsed(n.data));
} else if (!dropCallNodes) {
return callGraphNodeFor(n.data);
}
}).toList(growable: false);
for (var n in nodes) {
for (var succ in n.succ) {
final from = newNodes[n.id];
final to = newNodes[succ.id];
if (from != null && to != null) {
from.connectTo(to);
}
}
}
return CallGraph._(program, graphNodesByData.values.toList(growable: false),
graphNodeByEntityId);
}