UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

77 lines 3.18 kB
"use strict"; 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