UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

138 lines 6.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Dataflow = void 0; const graph_1 = require("./graph"); const edge_1 = require("./edge"); const dataflowgraph_builder_1 = require("./dataflowgraph-builder"); const dfg_get_origin_1 = require("../origin/dfg-get-origin"); const graph_helper_1 = require("./graph-helper"); const call_graph_1 = require("./call-graph"); /** * This is the root helper object to work with the {@link DataflowGraph}. * * - {@link Dataflow.visualize} - for visualization helpers (e.g., rendering the DFG as a mermaid graph), * - {@link Dataflow.views} - for working with specific views of the dataflow graph (e.g., the call graph), * - {@link Dataflow.edge} - for working with the edges in the dataflow graph, */ exports.Dataflow = { name: 'Dataflow', /** * Maps to flowR's main graph object to store and manipulate the dataflow graph * @see {@link DataflowGraph} */ graph: graph_1.DataflowGraph, ...graph_helper_1.GraphHelper, /** * Maps to flowR's dataflow edge helper to work with the edges in the dataflow graph */ edge: edge_1.DfEdge, /** * Dispatches to helper objects that relate to (sub-) views of the dataflow graph, e.g. the call graph. */ views: { /** * Maps to flowR's helper object for the call-graph */ callGraph: call_graph_1.CallGraph, }, /** * Dispatches to helper functions to create new dataflow graphs, e.g. from a pipeline or an empty graph. */ create: { /** * Creates an empty dataflow graph with the given id map (or a new one if not provided). * @see {@link emptyGraph} */ empty: dataflowgraph_builder_1.emptyGraph }, /** * Returns the origin of a vertex in the dataflow graph * @see {@link getOriginInDfg} - for the underlying function */ origin: dfg_get_origin_1.getOriginInDfg, /** * Only returns the sub-part of the graph that is determined by the given selection. * In other words, this will return a graph with only vertices that are part of the selected ids, * and edges that are between such selected vertices. * @param graph - the dataflow graph to slice for * @param select - the ids to select in the reduced graph * @param includeMissingTargets - if set to true, this will include edges which target vertices that are not selected! */ reduceGraph(graph, select, includeMissingTargets = false) { const df = new graph_1.DataflowGraph(graph.idMap); const roots = graph.rootIds(); // if the graph has no root ids all selected vertices are non-root in this case we just break the fdef selection and promote all to root! const selectedRoots = roots.intersection(select); const forceRoot = selectedRoots.size === 0; for (const [id, vtx] of graph.vertices(true)) { if (select.has(id)) { df.addVertex(vtx, vtx.environment, forceRoot || roots.has(id)); } } for (const [from, targets] of graph.edges()) { if (!select.has(from)) { continue; } for (const [tar, { types }] of targets.entries()) { if (!includeMissingTargets && !select.has(tar)) { continue; } df.addEdge(from, tar, types); } } for (const u of graph.unknownSideEffects) { if (typeof u === 'object' && select.has(u.id)) { df.markIdForUnknownSideEffects(u.id, u.linkTo); } else if (select.has(u)) { df.markIdForUnknownSideEffects(u); } } return df; }, /** * Given the id of a vertex (usually a variable use), * this returns a reachable provenance set by calculating a non-interprocedural and non-context sensitive backward slice, but stopping at the given ids! * You can obtain the corresponding graph using {@link Dataflow.reduceGraph}. * @param id - The id to use as a seed for provenance calculation * @param graph - The graph to perform the provenance calculation on * @param consider - The ids to restrict the calculation too (e.g., the ids contained within a function definition to restrict the analysis to) * @see {@link Dataflow.provenanceGraph} - for a convenience wrapper to directly obtain the graph of the provenance. */ provenance(id, graph, consider) { const queue = [id]; const visited = new Set(); const followEdges = edge_1.EdgeType.Calls | edge_1.EdgeType.Reads | edge_1.EdgeType.Returns | edge_1.EdgeType.Argument | edge_1.EdgeType.DefinedBy | edge_1.EdgeType.DefinedByOnCall; while (queue.length > 0) { const nodeId = queue.pop(); if (nodeId === undefined || visited.has(nodeId) || (consider && !consider.has(nodeId))) { continue; } visited.add(nodeId); const vtx = graph.get(nodeId); if (vtx === undefined) { continue; } for (const [to, types] of vtx[1]) { if (edge_1.DfEdge.includesType(types, followEdges)) { queue.push(to); } } for (const cd of vtx[0].cds ?? []) { queue.push(cd.id); } } return visited; }, /** * A convenience wrapper for {@link Dataflow.reduceGraph|reducing} the {@link Dataflow.provenance|provenance} of a graph. * @param id - The id to use as a seed for provenance calculation * @param graph - The graph to perform the provenance calculation on * @param consider - The ids to restrict the calculation too (e.g., the ids contained within a function definition to restrict the analysis to) * @see {@link Dataflow.provenance} */ provenanceGraph(id, graph, consider) { return exports.Dataflow.reduceGraph(graph, exports.Dataflow.provenance(id, graph, consider)); } }; //# sourceMappingURL=df-helper.js.map