@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
798 lines • 36.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ControlFlowGraph = exports.CfgEdge = exports.CfgVertex = exports.CfgVertexType = void 0;
exports.emptyControlFlowInformation = emptyControlFlowInformation;
const node_id_1 = require("../r-bridge/lang-4.x/ast/model/processing/node-id");
const convert_values_1 = require("../r-bridge/lang-4.x/convert-values");
const assert_1 = require("../util/assert");
/**
* The type of a vertex in the {@link ControlFlowGraph}.
* Please use the helper object (e.g. {@link CfgVertex#getType|getType()}) to work with vertices instead of directly accessing the properties.
*/
var CfgVertexType;
(function (CfgVertexType) {
/**
* The explicit exit-nodes to ensure the hammock property.
* @see {@link CfgVertex.makeMarker|CfgVertex.makeMarker()} - for a helper function to create end marker vertices
*/
CfgVertexType[CfgVertexType["Marker"] = 0] = "Marker";
/**
* something like an if, assignment, ... even though in the classical sense of R they are still expressions
* @see {@link CfgVertex.makeStatement|CfgVertex.makeStatement()} - for a helper function to create statement vertices
*/
CfgVertexType[CfgVertexType["Statement"] = 1] = "Statement";
/**
* something like an addition, ...
* @see {@link CfgVertex.makeExpression|CfgVertex.makeExpression()} - for a helper function to create expression vertices
*/
CfgVertexType[CfgVertexType["Expression"] = 2] = "Expression";
/**
* a (as far as R allows this) 'basic' block
* @see {@link CfgVertex.makeBlock|CfgVertex.makeBlock()} - for a helper function to create basic block vertices
*/
CfgVertexType[CfgVertexType["Block"] = 3] = "Block";
})(CfgVertexType || (exports.CfgVertexType = CfgVertexType = {}));
/**
* Helper object for {@link CfgVertex} - a vertex in the {@link ControlFlowGraph} that may have markers attached to it (e.g., for function calls).
*/
exports.CfgVertex = {
/**
* Create a new expression vertex with the given id, children, call targets, and markers.
* @param id - the id of the vertex, which should directly relate to the AST node
* @param children - child nodes attached to this one
* @param callTargets - if the vertex calls a function, this links all targets of this call
* @param mid - the ids of the mid-markers attached to this vertex, which should directly relate to the AST nodes of the mid markers
* @param end - the ids of the end-markers attached to this vertex, which should directly relate to the AST nodes of the end markers
* @see {@link CfgVertex#isExpression|isExpression()} - for a way to check whether a vertex is an expression vertex
*/
makeExpression(id, { children, mid, end, callTargets } = {}) {
if (children === undefined && callTargets === undefined) {
if (mid === undefined && end === undefined) {
return [CfgVertexType.Expression, id];
}
else {
return [CfgVertexType.Expression, id, mid, end];
}
}
return [CfgVertexType.Expression, id, mid, end, children, callTargets];
},
/**
* A convenience function to create a new expression vertex with a canonical end marker ({@link CfgVertex#toExitId|toExitId()}) based on the given id and the given id as root id for the end marker.
* @param id - the id of the vertex, which should directly relate to the AST node
* @param children - child nodes attached to this one
* @param callTargets - if the vertex calls a function, this links all targets of this call
* @param mid - the ids of the mid-markers attached to this vertex, which should directly relate to the AST nodes of the mid markers
* @see {@link CfgVertex#makeExpression|makeExpression()} - for a way to create expression vertices with a custom end marker
* @see {@link CfgVertex#makeExitMarker|makeExitMarker()} - for a helper function to create end marker vertices with a canonical id#
* @see {@link CfgVertex#toExitId|toExitId()} - for a way to convert the given id to a canonical end marker id
*/
makeExpressionWithEnd(id, { children, mid, callTargets } = {}) {
return exports.CfgVertex.makeExpression(id, { children, mid, end: [exports.CfgVertex.toExitId(id)], callTargets });
},
/**
* A convenience function to create a new statement vertex with a canonical end marker ({@link CfgVertex#toExitId|toExitId()}) based on the given id and the given id as root id for the end marker.
* @param id - the id of the vertex, which should directly relate to the AST node
* @param children - child nodes attached to this one
* @param callTargets - if the vertex calls a function, this links all targets of this call
* @param mid - the ids of the mid-markers attached to this vertex, which should directly relate to the AST nodes of the mid markers
* @see {@link CfgVertex#makeExpression|makeExpression()} - for a way to create expression vertices with a custom end marker
* @see {@link CfgVertex#makeExitMarker|makeExitMarker()} - for a helper function to create end marker vertices with a canonical id#
* @see {@link CfgVertex#toExitId|toExitId()} - for a way to convert the given id to a canonical end marker id
*/
makeStatementWithEnd(id, { children, mid, callTargets } = {}) {
return exports.CfgVertex.makeStatement(id, { children, mid, end: [exports.CfgVertex.toExitId(id)], callTargets });
},
/**
* Create a new statement vertex with the given id, children, call targets, and markers.
* @param id - the id of the vertex, which should directly relate to the AST node
* @param children - child nodes attached to this one
* @param callTargets - if the vertex calls a function, this links all targets of this call
* @param mid - the ids of the mid-markers attached to this vertex, which should directly relate to the AST nodes of the mid markers
* @param end - the ids of the end-markers attached to this vertex, which should directly relate to the AST nodes of the end markers
* @see {@link CfgVertex#isStatement|isStatement()} - for a way to check whether a vertex is a statement vertex
*/
makeStatement(id, { children, mid, end, callTargets } = {}) {
if (children === undefined && callTargets === undefined) {
if (mid === undefined && end === undefined) {
return [CfgVertexType.Statement, id];
}
else {
return [CfgVertexType.Statement, id, mid, end];
}
}
return [CfgVertexType.Statement, id, mid, end, children, callTargets];
},
/**
* A convenience function to create a new vertex which is either a statement or an expression.
*/
makeExprOrStm(id, type, { children, mid, end, callTargets } = {}) {
if (children === undefined && callTargets === undefined) {
if (mid === undefined && end === undefined) {
return [type, id];
}
else {
return [type, id, mid, end];
}
}
return [type, id, mid, end, children, callTargets];
},
/**
* Create a new marker vertex with the given id, root id, children, and call targets.
* @param id - the id of the vertex, which should directly relate to the AST node
* @param rootId - the id of the AST node this end marker corresponds to
* @see {@link CfgVertex#isMarker|isMarker()} - for a way to check whether a vertex is an end marker vertex
* @see {@link CfgVertex#getRootId|getRootId()} - for a way to get the root id of an end marker vertex
* @see {@link CfgVertex#makeExitMarker|makeExitMarker()} - for a helper function to create end marker vertices with a canonical id
*/
makeMarker(id, rootId) {
if (exports.CfgVertex.fromExitId(id) === rootId) {
return id;
}
else {
return [CfgVertexType.Marker, id, rootId];
}
},
/**
* A convenience function to create a new marker vertex with a canonical id ({@link CfgVertex#toExitId|toExitId()}) based on the given id and the given id as root id.
* @see {@link CfgVertex#makeMarker|makeMarker()} - for a way to create end marker vertices with a custom id
*/
makeExitMarker(id) {
return exports.CfgVertex.toExitId(id);
},
/**
* Create a new basic block vertex with the given id, elements, children, and call targets.
* @param id - the id of the vertex, which should directly relate to the AST node
* @param elems - the vertices that are part of this block, only connected by FDs, vertices should never occur in multiple bbs
* @see {@link CfgVertex#isBlock|isBlock()} - for a way to check whether a vertex is a basic block vertex
*/
makeBlock(id, elems) {
return [CfgVertexType.Block, id, elems];
},
/**
* Check whether the given vertex is an expression vertex.
* @see {@link CfgVertex#makeExpression|makeExpression()} - for a way to create expression vertices
* @see {@link CfgVertex#getType|getType()} - for a way to get the type of a vertex instead of checking against a given type
*/
isExpression(vertex) {
return Array.isArray(vertex) && vertex[0] === CfgVertexType.Expression;
},
/**
* Check whether the given vertex is a statement vertex.
* @see {@link CfgVertex#makeStatement|makeStatement()} - for a way to create statement vertices
* @see {@link CfgVertex#getType|getType()} - for a way to get the type of a vertex instead of checking against a given type
*/
isStatement(vertex) {
return Array.isArray(vertex) && vertex[0] === CfgVertexType.Statement;
},
/**
* Check whether the given vertex is an end marker vertex.
* @see {@link CfgVertex#makeMarker|makeMarker()} - for a way to create end marker vertices
* @see {@link CfgVertex#getType|getType()} - for a way to get the type of a vertex instead of checking against a given type
*/
isMarker(vertex) {
return vertex !== undefined && (!Array.isArray(vertex) || vertex[0] === CfgVertexType.Marker);
},
/**
* Check whether the given vertex is a basic block vertex.
* @see {@link CfgVertex#makeBlock|makeBlock()} - for a way to create basic block vertices
* @see {@link CfgVertex#getType|getType()} - for a way to get the type of a vertex instead of checking against a given type
*/
isBlock(vertex) {
return Array.isArray(vertex) && vertex[0] === CfgVertexType.Block;
},
/**
* Get the type of the given vertex.
* @example
* ```ts
* const vertex: CfgVertex = CfgVertex.makeExpr('node-1')
* console.log(CfgVertex.getType(vertex)); // Output: CfgVertexType.Expression
* ```
* @see {@link CfgVertex#isExpression|isExpression()}, {@link CfgVertex#isStatement|isStatement()}, {@link CfgVertex#isMarker|isMarker()}, {@link CfgVertex#isBlock|isBlock()} - for ways to check the type of a vertex against a specific type instead of getting the type and checking it against a specific type
* @see {@link CfgVertex#getId|getId()} - for a way to get the id of a vertex
* @see {@link CfgVertex#typeToString|typeToString()} - for a way to convert the type of a vertex to a string for easier debugging and visualization
*/
getType(vertex) {
return Array.isArray(vertex) ? vertex[0] : CfgVertexType.Marker;
},
/**
* Convert the given vertex type to a string for easier debugging and visualization.
* @see {@link CfgVertexType} - for the possible vertex types
* @see {@link CfgVertex#getType|getType()} - for a way to get the type of a vertex and convert it to a string
*/
typeToString(type) {
switch (type) {
case CfgVertexType.Marker:
return 'marker';
case CfgVertexType.Statement:
return 'statement';
case CfgVertexType.Expression:
return 'expression';
case CfgVertexType.Block:
return 'block';
default:
(0, assert_1.assertUnreachable)(type);
}
},
/**
* Get the id of the given vertex, which should directly relate to the AST node.
* @example
* ```ts
* const vertex: CfgVertex = CfgVertex.makeExpr('node-1')
* console.log(CfgVertex.getId(vertex)); // Output: 'node-1'
* ```
* @see {@link CfgVertex#getType|getType()} - for a way to get the type of a vertex
* @see {@link CfgVertex#getRootId|getRootId()} - for a way to get the root id of a vertex
*/
getId(vertex) {
return vertex === undefined ? undefined : (Array.isArray(vertex) ? vertex[1] : vertex);
},
/**
* Check whether two vertices are equal, i.e., they have the same type, id, and if they are basic block vertices, they also have the same elements in the same order.
*/
equal(a, b) {
if (!Array.isArray(a) || !Array.isArray(b)) {
return a === b;
}
else if (a === b) {
return true;
}
else if (a[0] !== b[0] || a[1] !== b[1]) {
return false;
}
else if (a[0] === CfgVertexType.Block && b[0] === CfgVertexType.Block) {
return a[2].length === b[2].length && a[2].every((e, i) => exports.CfgVertex.equal(e, b[2][i]));
}
else if (a[0] === CfgVertexType.Marker && b[0] === CfgVertexType.Marker) {
return a[2] === b[2];
}
return true;
},
/**
* Get the root id of a vertex, i.e., the id of the AST node it corresponds to.
* For normal vertices, this is the same as the id of the vertex itself, for end marker vertices, this is the root id stored in the vertex.
* @see {@link CfgVertex#unpackRoot|unpackRoot()} - for a way to unpack the root id of a marker vertex
*/
getRootId(vertex) {
return exports.CfgVertex.isMarker(vertex) ? exports.CfgVertex.unpackRootId(vertex) : vertex[1];
},
/**
* Unpack the root id of a marker vertex, i.e., get the root id stored in the vertex or derive it from the canonical id if it is not explicitly stored.
* @see {@link CfgVertex#getRootId|getRootId()} - for a way to get the root id of a vertex, which uses this function for marker vertices
*/
unpackRootId(vertex) {
return Array.isArray(vertex) ? vertex[2] ?? exports.CfgVertex.fromExitId(vertex[1]) : exports.CfgVertex.fromExitId(vertex);
},
/**
* Get the elements of a basic block vertex, i.e., the vertices that are part of this block, only connected by FDs, vertices should never occur in multiple bbs.
* @see {@link CfgVertex#isBlock|isBlock()} - for a way to check whether a vertex is a basic block vertex before trying to get the elements
* @see {@link CfgVertex#setBasicBlockElements|setBasicBlockElements()} - for a way to set the elements of a basic block vertex
*/
getBasicBlockElements(vertex) {
return vertex[2];
},
/**
* **Sets in-place**
* Set the elements of a basic block vertex, i.e., the vertices that are part of this block, only connected by FDs, vertices should never occur in multiple bbs.
* @see {@link CfgVertex#isBlock|isBlock()} - for a way to check whether a vertex is a basic block vertex before trying to set the elements
* @see {@link CfgVertex#getBasicBlockElements|getBasicBlockElements()} - for a way to get the elements of a basic block vertex
*/
setBasicBlockElements(vertex, elems) {
vertex[2] = elems;
},
/**
* Get the ids of the mid-markers attached to this vertex, which should directly relate to the AST nodes of the mid markers.
* @see {@link CfgVertex#getMid|getMid()} - for a way to get the ids of the mid-markers attached to this vertex
* @see {@link CfgVertex#setEnd|setEnd()} - for a way to set the ids of the end-markers attached to this vertex
*/
getEnd(vertex) {
if (vertex === undefined) {
return undefined;
}
const type = exports.CfgVertex.getType(vertex);
if (type === CfgVertexType.Statement || type === CfgVertexType.Expression) {
return vertex[3];
}
return undefined;
},
/**
* **Sets in-place**
* Set the ids of the end-markers attached to this vertex, which should directly relate to the AST nodes of the end markers.
* @see {@link CfgVertex#getEnd|getEnd()} - for a way to get the ids of the end-markers attached to this vertex
*/
setEnd(vertex, endMarkers) {
vertex[3] = endMarkers;
},
/**
* Get the ids of the mid-markers attached to this vertex, which should directly relate to the AST nodes of the mid markers.
*/
getMid(vertex) {
if (vertex === undefined) {
return undefined;
}
const type = exports.CfgVertex.getType(vertex);
if (type === CfgVertexType.Statement || type === CfgVertexType.Expression) {
return vertex[2];
}
return undefined;
},
/**
* **Sets in-place**
* Set the ids of the mid-markers attached to this vertex, which should directly relate to the AST nodes of the mid markers.
* @see {@link CfgVertex#getMid|getMid()} - for a way to get the ids of the mid-markers attached to this vertex
* @see {@link CfgVertex#setEnd|setEnd()} - for a way to set the ids of the end-markers attached to this vertex
*/
setMid(vertex, midMarkers) {
vertex[2] = midMarkers;
},
/**
* Converts the given id to a, canonical, basic block lift (i.e., it adds 'bb-' as a prefix).
*/
toBasicBlockId(id) {
return `bb-${id}`;
},
/**
* Converts the given id to a canonical, end marker lift (i.e., it adds '-end' as a suffix).
* @see {@link CfgVertex#fromExitId|fromExitId()} - for a way to convert the given id from a canonical end marker lift to the original id (i.e., it removes '-end' as a suffix if it is present)
*/
toExitId(id) {
return `${id}-e`;
},
/**
* Converts the given id from a canonical end marker lift to the original id (i.e., it removes '-end' as a suffix if it is present).
* @see {@link CfgVertex#toExitId|toExitId()} - for a way to convert the given id to a canonical end marker lift (i.e., it adds '-end' as a suffix)
*/
fromExitId(exitId) {
if (typeof exitId === 'string' && exitId.endsWith('-e')) {
return node_id_1.NodeId.normalize(exitId.slice(0, -2));
}
else {
return exitId;
}
},
/**
* Get the call targets of a statement or expression vertex, which links all targets of this call.
*/
getCallTargets(vertex) {
if (vertex === undefined) {
return undefined;
}
const type = exports.CfgVertex.getType(vertex);
if (type === CfgVertexType.Statement || type === CfgVertexType.Expression) {
return vertex[5];
}
return undefined;
},
/**
* **Sets in-place**
* Set the call targets of a statement or expression vertex, which links all targets of this call.
* @see {@link CfgVertex#getCallTargets|getCallTargets()} - for a way to get the call targets of a statement or expression vertex
* @see {@link CfgVertex#mapCallTargets|mapCallTargets()} - for a way to map the call targets of a statement or expression vertex to new call targets
*/
setCallTargets(vertex, callTargets) {
vertex[5] = callTargets;
},
/**
* Map the call targets of a statement or expression vertex, which links all targets of this call, to new call targets using the given mapping function.
* @see {@link CfgVertex#getCallTargets|getCallTargets()} - for a way to get the call targets of a statement or expression vertex
* @see {@link CfgVertex#setCallTargets|setCallTargets()} - for a way to set the call targets of a statement or expression vertex to new call targets
*/
mapCallTargets(vertex, mapFn) {
const currentTargets = exports.CfgVertex.getCallTargets(vertex);
const newTargets = mapFn(currentTargets);
exports.CfgVertex.setCallTargets(vertex, newTargets);
},
/**
* Get the children of a statement or expression vertex, i.e., the child nodes attached to this one.
*/
getChildren(vertex) {
if (vertex === undefined) {
return undefined;
}
const type = exports.CfgVertex.getType(vertex);
if (type === CfgVertexType.Statement || type === CfgVertexType.Expression) {
return vertex[4];
}
return undefined;
}
};
/**
* Helper object for {@link CfgEdge} - an edge in the {@link ControlFlowGraph}.
*/
exports.CfgEdge = {
/**
* Check whether the given edge is a flow dependency edge.
*/
isFlowDependency(edge) {
return edge === 0 /* CfgEdgeType.Fd */;
},
/**
* Check whether the given edge is a control dependency edge.
*/
isControlDependency(edge) {
return Array.isArray(edge) && edge.length === 2;
},
/**
* Create a flow dependency edge.
*/
makeFd() {
return 0 /* CfgEdgeType.Fd */;
},
/**
* Create a control dependency edge with the given cause and condition.
* @param controlId - the id of the vertex that causes the control dependency
* @param whenTrue - whether the control dependency is satisfied with a true condition or is it negated (e.g., else-branch)
* @see {@link CfgEdge#makeCdTrue|makeCdTrue()} - for a version of this function that assumes the control dependency is satisfied with a true condition
* @see {@link CfgEdge#makeCdFalse|makeCdFalse()} - for a version of this function that assumes the control dependency is negated (e.g., else-branch)
*/
makeCd(controlId, whenTrue) {
return [controlId, whenTrue];
},
/**
* Create a control dependency edge with the given cause and a true condition.
* @param controlId - the id of the vertex that causes the control dependency
* @see {@link CfgEdge#makeCd|makeCd()} - for a version of this function that allows to specify the condition as well
*/
makeCdTrue(controlId) {
return [controlId, convert_values_1.RTrue];
},
/**
* Create a control dependency edge with the given cause and a negated condition (e.g., else-branch).
* @param controlId - the id of the vertex that causes the control dependency
* @see {@link CfgEdge#makeCd|makeCd()} - for a version of this function that allows to specify the condition as well
*/
makeCdFalse(controlId) {
return [controlId, convert_values_1.RFalse];
},
/**
* Get the cause of a control dependency edge, i.e., the id of the vertex that causes the control dependency.
* If the edge is not a control dependency edge, this returns undefined.
*
* This is the pendant of {@link CfgEdge#isControlDependency|isControlDependency()} on a {@link CfgEdge}.
* @see {@link CfgEdge#unpackCause|unpackCause()} - for a version of this function that assumes the edge is a control dependency edge and hence does not return undefined
*/
getCause(edge) {
if (exports.CfgEdge.isControlDependency(edge)) {
return edge[0];
}
else {
return undefined;
}
},
/**
* Get the cause of a control dependency edge, i.e., the id of the vertex that causes the control dependency.
*/
unpackCause(edge) {
return edge[0];
},
/**
* Get whether the control dependency edge is satisfied with a true condition or is it negated (e.g., else-branch).
* If the edge is not a control dependency edge, this returns undefined.
*
* This is the pendant of {@link CfgEdge#isControlDependency|isControlDependency()} on a {@link CfgEdge}.
* @see {@link CfgEdge#unpackWhen|unpackWhen()} - for a version of this function that assumes the edge is a control dependency edge and hence does not return undefined
*/
getWhen(edge) {
if (exports.CfgEdge.isControlDependency(edge)) {
return edge[1];
}
else {
return undefined;
}
},
/**
* Get whether the control dependency edge is satisfied with a true condition or is it negated (e.g., else-branch).
*/
unpackWhen(edge) {
return edge[1];
},
/**
* Check whether two edges are equal.
*/
equals(a, b) {
if (exports.CfgEdge.isFlowDependency(a) && exports.CfgEdge.isFlowDependency(b)) {
return true;
}
else if (exports.CfgEdge.isControlDependency(a) && exports.CfgEdge.isControlDependency(b)) {
return a[0] === b[0] && a[1] === b[1];
}
return false;
},
/**
* Check whether the given edge is of the given type.
* @see {@link CfgEdge#getType|getType()} - for a version of this function that returns the type of the edge instead of checking against a given type
*/
isOfType(edge, type) {
return exports.CfgEdge.getType(edge) === type;
},
/**
* Get the type of the given edge.
* @see {@link CfgEdge#isOfType|isOfType()} - for a version of this function that checks whether the edge is of a given type
*/
getType(edge) {
return exports.CfgEdge.isFlowDependency(edge) ? 0 /* CfgEdgeType.Fd */ : 1 /* CfgEdgeType.Cd */;
},
/**
* Provide a string representation of the given edge, e.g., for debugging or visualization purposes.
* @see {@link CfgEdge#toString|toString()} - for a version of this function that also includes the details of the edge (e.g., cause and condition for control dependency edges)
*/
typeToString(edge) {
if (exports.CfgEdge.isFlowDependency(edge)) {
return 'FD';
}
else {
return 'CD';
}
},
/**
* Provide a string representation of the given edge, including its details (e.g., cause and condition for control dependency edges), e.g., for debugging or visualization purposes.
* @see {@link CfgEdge#typeToString|typeToString()} - for a version of this function that only includes the type of the edge
*/
toString(edge) {
if (exports.CfgEdge.isFlowDependency(edge)) {
return 'FD';
}
else {
return `CD(${edge[0]}, ${edge[1] === convert_values_1.RTrue ? 'T' : 'F'})`;
}
}
};
/**
* This class represents the control flow graph of an R program.
* The control flow may be hierarchical when confronted with function definitions (see {@link CfgVertex} and {@link CFG#rootVertexIds|rootVertexIds()}).
*
* There are two very simple visitors to traverse a CFG:
* - {@link visitCfgInOrder} visits the graph in the order of the vertices
* - {@link visitCfgInReverseOrder} visits the graph in reverse order
*
* If you want to prohibit modification, please refer to the {@link ReadOnlyControlFlowGraph} interface.
*/
class ControlFlowGraph {
roots = new Set();
/** Nesting-Independent vertex information, mapping the id to the vertex */
vtxInfos = new Map();
/** the basic block children map contains a mapping of ids to all vertices that are nested in basic blocks, mapping them to the Id of the block they appear in */
bbChildren = new Map();
/** basic block agnostic edges */
edgeInfos = new Map();
/** reverse edges for bidirectional mapping */
revEdgeInfos = new Map();
/** used as an optimization to avoid unnecessary lookups */
_mayBB = false;
/**
* Add a new vertex to the control flow graph.
* @see {@link ControlFlowGraph#addEdge|addEdge()} - to add an edge
*/
addVertex(vertex, rootVertex = true) {
const vid = exports.CfgVertex.getId(vertex);
if (exports.CfgVertex.isBlock(vertex)) {
this._mayBB = true;
const elems = exports.CfgVertex.getBasicBlockElements(vertex);
if (elems.some(e => {
const eid = exports.CfgVertex.getId(e);
return this.bbChildren.has(eid) || this.roots.has(eid);
})) {
throw new Error(`Vertex ${vid} contains vertices that are already part of the graph`);
}
for (const elem of elems) {
this.bbChildren.set(exports.CfgVertex.getId(elem), vid);
}
}
this.vtxInfos.set(vid, vertex);
if (rootVertex) {
this.roots.add(vid);
}
return this;
}
/**
* Add a new edge to the control flow graph.
* @see {@link ControlFlowGraph#addVertex|addVertex()} - to add vertices
* @see {@link ControlFlowGraph#addEdges|addEdges()} - to add multiple edges at once
*/
addEdge(from, to, edge) {
const edgesFrom = this.edgeInfos.get(from) ?? new Map();
if (!this.edgeInfos.has(from)) {
this.edgeInfos.set(from, edgesFrom);
}
edgesFrom.set(to, edge);
const edgesTo = this.revEdgeInfos.get(to) ?? new Map();
if (!this.revEdgeInfos.has(to)) {
this.revEdgeInfos.set(to, edgesTo);
}
edgesTo.set(from, edge);
return this;
}
/**
* Add multiple edges from a given source vertex to the control flow graph.
*/
addEdges(from, to) {
for (const [toNode, edge] of to) {
this.addEdge(from, toNode, edge);
}
return this;
}
outgoingEdges(node) {
return this.edgeInfos.get(node);
}
ingoingEdges(node) {
return this.revEdgeInfos.get(node);
}
rootIds() {
return this.roots;
}
vertices(includeBasicBlockElements = true) {
if (includeBasicBlockElements) {
const all = new Map(this.vtxInfos);
for (const [id, block] of this.bbChildren.entries()) {
const blockVertex = all.get(block);
if (blockVertex === undefined || !exports.CfgVertex.isBlock(blockVertex)) {
continue;
}
const elems = exports.CfgVertex.getBasicBlockElements(blockVertex);
const elem = elems.find(e => exports.CfgVertex.getId(e) === id);
if (elem !== undefined) {
all.set(id, elem);
}
}
return all;
}
else {
return this.vtxInfos;
}
}
getBasicBlock(elemId) {
const block = this.bbChildren.get(elemId);
if (block === undefined) {
return undefined;
}
const blockVertex = this.vtxInfos.get(block);
if (blockVertex === undefined || !exports.CfgVertex.isBlock(blockVertex)) {
return undefined;
}
return blockVertex;
}
edges() {
return this.edgeInfos;
}
/**
* Retrieve a vertex by its id.
*/
getVertex(id, includeBlocks = true) {
const res = this.vtxInfos.get(id);
if (res || !includeBlocks) {
return res;
}
const block = this.bbChildren.get(id);
if (block === undefined) {
return undefined;
}
const blockVertex = this.vtxInfos.get(block);
if (blockVertex === undefined || !exports.CfgVertex.isBlock(blockVertex)) {
return undefined;
}
const elems = exports.CfgVertex.getBasicBlockElements(blockVertex);
return elems.find(e => exports.CfgVertex.getId(e) === id);
}
hasVertex(id, includeBlocks = true) {
return this.vtxInfos.has(id) || (this._mayBB && includeBlocks && this.bbChildren.has(id));
}
mayHaveBasicBlocks() {
return this._mayBB;
}
/**
* This removes the vertex and all edges to and from it.
* @param id - the id of the vertex to remove
* @see {@link ControlFlowGraph#addVertex|addVertex()} - to add a vertex
* @see {@link ControlFlowGraph#removeEdge|removeEdge()} - to remove a specific edge
*/
removeVertex(id) {
this.vtxInfos.delete(id);
this.edgeInfos.delete(id);
this.revEdgeInfos.delete(id);
this.bbChildren.delete(id);
// remove all bbChildren with id as target
for (const [a, b] of this.bbChildren.entries()) {
if (b === id) {
this.bbChildren.delete(a);
}
}
for (const edges of this.edgeInfos.values()) {
edges.delete(id);
}
for (const edges of this.revEdgeInfos.values()) {
edges.delete(id);
}
this.roots.delete(id);
return this;
}
/**
* Removes a all direct edges between `from` and `to` from the control flow graph.
* @see {@link ControlFlowGraph#addEdge|addEdge()} - to add an edge
* @see {@link ControlFlowGraph#removeVertex|removeVertex()} - to remove a vertex and all its edges
*/
removeEdge(from, to) {
const edgesFrom = this.edgeInfos.get(from);
if (edgesFrom) {
edgesFrom.delete(to);
if (edgesFrom.size === 0) {
this.edgeInfos.delete(from);
}
}
const edgesTo = this.revEdgeInfos.get(to);
if (edgesTo) {
edgesTo.delete(from);
if (edgesTo.size === 0) {
this.revEdgeInfos.delete(to);
}
}
return this;
}
/** merges b into a */
mergeTwoBasicBlocks(a, b) {
const aVertex = this.getVertex(a);
const bVertex = this.getVertex(b);
if (!aVertex || !bVertex || !exports.CfgVertex.isBlock(aVertex) || !exports.CfgVertex.isBlock(bVertex)) {
return this;
}
const bElems = exports.CfgVertex.getBasicBlockElements(bVertex);
exports.CfgVertex.setBasicBlockElements(aVertex, [...exports.CfgVertex.getBasicBlockElements(aVertex), ...bElems]);
// update cache
for (const elem of bElems) {
this.bbChildren.set(exports.CfgVertex.getId(elem), a);
}
// drop all edges from a to b
this.removeEdge(a, b);
const bOutgoing = this.outgoingEdges(b);
this.removeVertex(b);
// reroute all edge from b to a
for (const [to, edge] of bOutgoing ?? []) {
this.addEdge(a, to, edge);
}
return this;
}
/**
* **This Operation is in-place and modifies the current graph.**
* Merge another control flow graph into this one.
* @param other - the other control flow graph to merge into this one
* @param forceNested - should the other graph be assumed to be fully nested (e.g., within a function definition).
*
* This is the pendant of {@link DataflowGraph#mergeWith|mergeWith()} on a {@link DataflowGraph}.
*/
mergeWith(other, forceNested = false) {
this._mayBB ||= other._mayBB;
const roots = other.roots;
if (this._mayBB) {
for (const [id, node] of other.vtxInfos) {
this.addVertex(node, forceNested ? false : roots.has(id));
}
}
else {
for (const [id, node] of other.vtxInfos) {
this.vtxInfos.set(id, node);
}
if (!forceNested) {
for (const root of roots) {
this.roots.add(root);
}
}
}
for (const [from, edges] of other.edgeInfos) {
this.addEdges(from, edges);
}
return this;
}
}
exports.ControlFlowGraph = ControlFlowGraph;
/**
* Create an empty control flow information object.
*/
function emptyControlFlowInformation() {
return {
returns: [],
breaks: [],
nexts: [],
entryPoints: [],
exitPoints: [],
graph: new ControlFlowGraph()
};
}
//# sourceMappingURL=control-flow-graph.js.map