@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
111 lines • 3.91 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasicCfgGuidedVisitor = void 0;
const control_flow_graph_1 = require("./control-flow-graph");
const assert_1 = require("../util/assert");
/**
* In contrast to {@link visitCfgInOrder} and {@link visitCfgInReverseOrder}, this visitor is not a simple visitor
* and serves as the basis for a variety of more complicated visiting orders of the control flow graph.
* It includes features to provide additional information using the {@link NormalizedAst} and the {@link DataflowGraph}.
*
* Use {@link BasicCfgGuidedVisitor#start} to start the traversal.
*/
class BasicCfgGuidedVisitor {
config;
visited;
constructor(config) {
this.config = { ...config };
this.visited = new Map();
}
/**
* call this function to indicate that a node is to be considered visited.
* @returns `true` if the node was not visited before, `false` otherwise
*/
visitNode(node) {
if (this.visited.has(node)) {
return false;
}
this.visited.set(node, 1);
this.onVisitNode(node);
return true;
}
startVisitor(start) {
const graph = this.config.controlFlow.graph;
let getNext;
if (this.config.defaultVisitingOrder === 'forward') {
getNext = (node) => graph.ingoingEdges(node)?.keys().toArray().reverse();
}
else {
getNext = (node) => graph.outgoingEdges(node)?.keys();
}
const stack = Array.from(start);
while (stack.length > 0) {
const current = stack.pop();
if (!this.visitNode(current)) {
continue;
}
for (const next of getNext(current) ?? []) {
stack.push(next);
}
}
}
/**
* Start the visiting process.
*/
start() {
this.startVisitor(this.config.defaultVisitingOrder === 'forward' ? this.config.controlFlow.entryPoints : this.config.controlFlow.exitPoints);
}
/**
* Get the control flow vertex for the given node id or fail if it does not exist.
*/
getCfgVertex(id) {
return this.config.controlFlow.graph.getVertex(id);
}
onVisitNode(node) {
const vertex = this.getCfgVertex(node);
if (vertex === undefined) {
return;
}
const type = control_flow_graph_1.CfgVertex.getType(vertex);
switch (type) {
case control_flow_graph_1.CfgVertexType.Statement:
this.onStatementNode(vertex);
break;
case control_flow_graph_1.CfgVertexType.Expression:
this.onExpressionNode(vertex);
break;
case control_flow_graph_1.CfgVertexType.Marker:
this.onEndMarkerNode(vertex);
break;
case control_flow_graph_1.CfgVertexType.Block:
this.onBasicBlockNode(vertex);
break;
default:
(0, assert_1.assertUnreachable)(type);
}
}
onBasicBlockNode(node) {
const elems = control_flow_graph_1.CfgVertex.getBasicBlockElements(node);
if (this.config.defaultVisitingOrder === 'forward') {
for (const elem of elems.toReversed()) {
this.visitNode(control_flow_graph_1.CfgVertex.getId(elem));
}
}
else {
for (const elem of elems) {
this.visitNode(control_flow_graph_1.CfgVertex.getId(elem));
}
}
}
onStatementNode(_node) {
/* does nothing by default */
}
onExpressionNode(_node) {
/* does nothing by default */
}
onEndMarkerNode(_node) {
/* does nothing by default */
}
}
exports.BasicCfgGuidedVisitor = BasicCfgGuidedVisitor;
//# sourceMappingURL=basic-cfg-guided-visitor.js.map