@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
77 lines • 3.18 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.findAllClusters = findAllClusters;
const edge_1 = require("./graph/edge");
const assert_1 = require("../util/assert");
const vertex_1 = require("./graph/vertex");
/**
* Find all clusters in the given dataflow graph.
*/
function findAllClusters(graph) {
const clusters = [];
// we reverse the vertices since dependencies usually point "backwards" from later nodes
const ids = graph.vertices(true).map(([id]) => id).toArray().reverse();
/* walking the ids picks the same start nodes in the same order as draining the set did, without re-opening an
* iterator over the shrinking set for every cluster */
const notReached = new Set(ids);
/* `graph.ingoingEdges` rebuilds the reverse adjacency by scanning every edge of the graph, and clustering asks
* for it once per node; building it a single time turns that quadratic sweep into one pass */
const incoming = new Map();
for (const [source, outgoing] of graph.edges()) {
for (const [target, edge] of outgoing) {
const into = incoming.get(target);
if (into === undefined) {
incoming.set(target, new Map([[source, edge]]));
}
else {
into.set(source, edge);
}
}
}
for (const startNode of ids) {
if (!notReached.delete(startNode)) {
continue;
}
clusters.push({
startNode: startNode,
members: Array.from(makeCluster(graph, startNode, notReached, incoming)),
hasUnknownSideEffects: graph.unknownSideEffects.has(startNode)
});
}
return clusters;
}
/* one shared accumulator, filled iteratively: merging a set per node cost a copy of the whole cluster per member
* (quadratic in the cluster size), and the recursion ran as deep as the cluster was large */
function makeCluster(graph, from, notReached, incoming) {
const nodes = new Set([from]);
const pending = [from];
while (pending.length > 0) {
const current = pending.pop();
const info = graph.getVertex(current);
(0, assert_1.guard)(info !== undefined, () => `Vertex ${current} not found in graph`);
function reach(dest) {
if (notReached.delete(dest)) {
nodes.add(dest);
pending.push(dest);
}
}
// cluster function def exit points
if (vertex_1.FunctionDefinitionVertex.is(info)) {
for (const { nodeId } of info.exitPoints) {
reach(nodeId);
}
}
// cluster adjacent edges
for (const edges of [graph.outgoingEdges(current), incoming.get(current)]) {
for (const [dest, e] of edges ?? []) {
// don't cluster for function content if it isn't returned
if (edge_1.DfEdge.doesNotIncludeType(e, edge_1.EdgeType.Returns) && info.onlyBuiltin && info.name === '{') {
continue;
}
reach(dest);
}
}
}
return nodes;
}
//# sourceMappingURL=cluster.js.map