@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
138 lines • 6.21 kB
JavaScript
"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