@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
128 lines • 5.26 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.GraphHelper = void 0;
const dfg_1 = require("../../util/mermaid/dfg");
const quads_1 = require("./quads");
const diff_graph_1 = require("../../util/diff-graph");
const diff_dataflow_graph_1 = require("./diff-dataflow-graph");
const graph_1 = require("./graph");
const assert_1 = require("../../util/assert");
const parse_1 = require("../../slicing/criterion/parse");
const edge_1 = require("./edge");
const defaultmap_1 = require("../../util/collections/defaultmap");
/**
* The underlying functions which work for any graph* like view
* **Please do not use this object directly but use the helpers**
* - {@link Dataflow}
* - {@link CallGraph}
*/
exports.GraphHelper = {
/** Maps to the mermaid-centric visualization helper for dataflow graphs and their views */
visualize: {
/**
* Mermaid rendering helper for dataflow graphs
* - {@link DataflowMermaid.url}, {@link DataflowMermaid.raw} - to render the graph as a mermaid graph (e.g., in markdown or the mermaid live editor)
* - {@link DataflowMermaid.convert} - for the underlying transformation
* @see {@link DataflowMermaid}
*/
mermaid: dfg_1.DataflowMermaid,
quads: { convert: quads_1.df2quads }
},
/**
* Compare two dataflow graphs and return a report on the differences.
* If you simply want to check whether they equal, use {@link GraphDifferenceReport#isEqual|`<result>.isEqual()`}.
* @see {@link diffOfControlFlowGraphs} - for control flow graphs
*/
diffGraphs(left, right, config) {
if (left.graph === right.graph) {
return new diff_graph_1.GraphDifferenceReport();
}
const ctx = (0, diff_graph_1.initDiffContext)(left, right, config);
(0, diff_dataflow_graph_1.diffDataflowGraph)(ctx);
return ctx.report;
},
/**
* Inverts the given dataflow graph by reversing all edges.
*/
invertGraph(graph, cleanEnv) {
const invertedGraph = new graph_1.DataflowGraph(graph.idMap);
for (const [, v] of graph.vertices(true)) {
invertedGraph.addVertex(v, cleanEnv);
}
for (const [from, targets] of graph.edges()) {
for (const [to, { types }] of targets) {
invertedGraph.addEdge(to, from, types);
}
}
return invertedGraph;
},
/**
* Resolves the dataflow graph ids from slicing criterion form to ids.
* This returns a **new** graph with the resolved ids.
* The main use-case for this is testing - if you do not know/want to fix the specific id,
* you can use, e.g. `2@x` as a placeholder for the first x in the second line!
*/
resolveGraphCriteria(graph, ctx, idMap) {
const resolveMap = idMap ?? graph.idMap;
(0, assert_1.guard)(resolveMap !== undefined, 'idMap must be provided to resolve the graph');
const cache = new Map();
const resolve = (id) => {
const cached = cache.get(id);
if (cached !== undefined) {
return cached;
}
const resolved = parse_1.SlicingCriterion.tryParse(id, resolveMap) ?? id;
cache.set(id, resolved);
return resolved;
};
const resultGraph = new graph_1.DataflowGraph(resolveMap);
const roots = graph.rootIds();
/* recreate vertices */
for (const [id, vertex] of graph.vertices(true)) {
resultGraph.addVertex({
...vertex,
id: resolve(id)
}, ctx.env.makeCleanEnv(), roots.has(id));
}
/* recreate edges */
for (const [from, targets] of graph.edges()) {
for (const [to, info] of targets) {
for (const type of edge_1.DfEdge.splitTypes(info)) {
resultGraph.addEdge(resolve(from), resolve(to), type);
}
}
}
for (const unknown of graph.unknownSideEffects) {
if (typeof unknown === 'object') {
resultGraph.markIdForUnknownSideEffects(resolve(unknown.id), unknown.linkTo);
}
else {
resultGraph.markIdForUnknownSideEffects(resolve(unknown));
}
}
return resultGraph;
},
/**
* Determines whether there is a path from `from` to `to` in the given graph (via any edge type, only respecting direction)
*/
reaches(from, to, graph, knownReachability = new defaultmap_1.DefaultMap(() => new Set())) {
const visited = new Set();
const toVisit = [from];
while (toVisit.length > 0) {
const currentId = toVisit.pop();
if (visited.has(currentId)) {
continue;
}
if (currentId === to || knownReachability.get(currentId).has(to)) {
knownReachability.get(from).add(to);
return true;
}
visited.add(currentId);
for (const [tar] of graph.outgoingEdges(currentId) ?? []) {
toVisit.push(tar);
}
}
return false;
},
};
//# sourceMappingURL=graph-helper.js.map