UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

128 lines 5.26 kB
"use strict"; 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