UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

798 lines 36.2 kB
"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