UNPKG

@syntest/analysis-javascript

Version:

SynTest CFG JavaScript is a library for generating control flow graphs for the JavaScript language

739 lines 34.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ControlFlowGraphVisitor = void 0; const ast_visitor_javascript_1 = require("@syntest/ast-visitor-javascript"); const cfg_1 = require("@syntest/cfg"); const logging_1 = require("@syntest/logging"); class ControlFlowGraphVisitor extends ast_visitor_javascript_1.AbstractSyntaxTreeVisitor { get cfg() { if (!this._nodes.has("ENTRY")) { throw new Error("No entry node found"); } if (!this._nodes.has("SUCCESS_EXIT")) { throw new Error("No success exit node found"); } if (!this._nodes.has("ERROR_EXIT")) { throw new Error("No error exit node found"); } if (this._nodesList.length !== this._nodes.size) { throw new Error("Number of nodes dont match"); } const entryNode = this._nodes.get("ENTRY"); const successExitNode = this._nodes.get("SUCCESS_EXIT"); const errorExitNode = this._nodes.get("ERROR_EXIT"); if (this._currentParents[0] === "ENTRY") { // nothing added so we add } // connect last nodes to success exit this._connectToParents(successExitNode); // connect all return nodes to success exit for (const returnNode of this._returnNodes) { this._edges.push(this._createEdge(this._nodes.get(returnNode), successExitNode, cfg_1.EdgeType.NORMAL)); } // connect all throw nodes to error exit for (const throwNode of this._throwNodes) { this._edges.push(this._createEdge(this._nodes.get(throwNode), errorExitNode, cfg_1.EdgeType.EXCEPTION)); } if (this._regularBreakNodesStack.length > 0) { ControlFlowGraphVisitor.LOGGER.warn(`Found ${this._regularBreakNodesStack.length} break node stacks that are not connected to a loop`); } if (this._regularContinueNodesStack.length > 0) { ControlFlowGraphVisitor.LOGGER.warn(`Found ${this._regularContinueNodesStack.length} continue node stacks that are not connected to a loop`); } if (this._labeledBreakNodes.size > 0) { ControlFlowGraphVisitor.LOGGER.warn(`Found ${this._labeledBreakNodes.size} break node labels that are not connected to a label exit`); } if (this._labeledContinueNodes.size > 0) { ControlFlowGraphVisitor.LOGGER.warn(`Found ${this._labeledContinueNodes.size} continue node labels that are not connected to a label exit`); } return { graph: new cfg_1.ControlFlowGraph(entryNode, successExitNode, errorExitNode, this._nodes, this._edges), functions: this._functions.map((function_, index) => { if (this._functions.filter((f) => f.name === function_.name).length > 1) { function_.name = `${function_.name} (${index})`; } return function_; }), }; } constructor(filePath, syntaxForgiving) { super(filePath, syntaxForgiving); this.Block = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering block at ${this._getNodeId(path)}`); // we need to repeat this from the baseclass because we cannot use super.Program if (path.isProgram() && this._scopeIdOffset === undefined) { this._scopeIdOffset = this._getUidFromScope(path.scope); this._thisScopeStack.push(this._getUidFromScope(path.scope)); this._thisScopeStackNames.push("global"); } for (const statement of path.get("body")) { statement.visit(); } path.skip(); }; // functions this.Function = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering function at ${this._getNodeId(path)}`); if (!this._nodes.has(this._getNodeId(path))) { const node = this._createNode(path); this._connectToParents(node); this._currentParents = [node.id]; } const subVisitor = new ControlFlowGraphVisitor(this.filePath, this.syntaxForgiving); path.traverse(subVisitor); if (!subVisitor._nodes.has("ENTRY")) { throw new Error("Should not be possible"); } const name = path.has("id") ? path.get("id").node.name : path.has("key") ? path.get("key").node.name : "anonymous"; const cfp = subVisitor.cfg; this._functions.push({ id: this._getNodeId(path), name: name, graph: cfp.graph, }, // sub functions within this function ...cfp.functions); path.skip(); }; // actual control flow graph related nodes this.Statement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering statement: ${path.type}\tline: ${path.node.loc.start.line}\tcolumn: ${path.node.loc.start.column}`); if (this._nodes.has(this._getNodeId(path))) { throw new Error(`Id already used id: ${this._getNodeId(path)}`); } else { const node = this._createNode(path); this._connectToParents(node); this._currentParents = [node.id]; } }; this.Expression = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering Expression at ${this._getNodeId(path)}`); if (this._nodes.has(this._getNodeId(path))) { // just ignore } else if (!path.isLiteral() && !path.isIdentifier()) { const node = this._createNode(path); this._connectToParents(node); this._currentParents = [node.id]; } }; this.Conditional = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering IfStatement at ${this._getNodeId(path)}`); const branchNode = this._createNode(path); this._connectToParents(branchNode); this._currentParents = [branchNode.id]; const testNode = this._createNode(path.get("test")); this._connectToParents(testNode); this._currentParents = [testNode.id]; // consequent this._edgeType = cfg_1.EdgeType.CONDITIONAL_TRUE; let sizeBefore = this._nodesList.length; path.get("consequent").visit(); // there either is no consequent or it is empty if (sizeBefore === this._nodesList.length) { const consequent = this._createNode(path.get("consequent")); this._connectToParents(consequent); this._currentParents = [consequent.id]; } const consequentNodes = this._currentParents; // alternate this._currentParents = [testNode.id]; this._edgeType = cfg_1.EdgeType.CONDITIONAL_FALSE; sizeBefore = this._nodesList.length; if (path.has("alternate")) { path.get("alternate").visit(); } // there either is no alternate or it is empty if (sizeBefore === this._nodesList.length) { if (path.has("alternate")) { const alternate = this._createNode(path.get("alternate")); this._connectToParents(alternate); this._currentParents = [alternate.id]; } else { const alternate = this._createPlaceholderNode(path); this._connectToParents(alternate); this._currentParents = [alternate.id]; } } const alternateNodes = this._currentParents; this._currentParents = [...alternateNodes, ...consequentNodes]; path.skip(); }; // labels this.LabeledStatement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering LabeledStatement at ${this._getNodeId(path)}`); const label = path.get("label").node.name; this._labeledBreakNodes.set(label, new Set()); this._labeledContinueNodes.set(label, new Set()); const labelNode = this._createNode(path); this._connectToParents(labelNode); // body this._currentParents = [labelNode.id]; const beforeSize = this._nodes.size; path.get("body").visit(); // check if something was created if (beforeSize === this._nodes.size) { // empty body // create placeholder node const placeholderNode = this._createPlaceholderNode(path.get("body")); this._connectToParents(placeholderNode); this._currentParents = [placeholderNode.id]; } // exit const labelExit = this._createPlaceholderNode(path, true); // connect all break nodes to exit this._currentParents.push(...this._labeledBreakNodes.get(label)); this._connectToParents(labelExit); this._currentParents = [labelExit.id]; // connect all continue nodes to label entry for (const continueNode of this._labeledContinueNodes.get(label)) { this._edges.push(this._createEdge(this._nodes.get(continueNode), labelNode, cfg_1.EdgeType.BACK_EDGE)); } // remove labeled break/continues this._labeledBreakNodes.delete(label); this._labeledContinueNodes.delete(label); path.skip(); }; // loops this.DoWhileStatement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering DoWhileStatement at ${this._getNodeId(path)}`); const doWhileNode = this._createNode(path); this._connectToParents(doWhileNode); this._currentParents = [doWhileNode.id]; this._regularBreakNodesStack.push(new Set()); this._regularContinueNodesStack.push(new Set()); const size = this._nodesList.length; // body path.get("body").visit(); let firstBodyNode = this._nodesList[size]; if (firstBodyNode === undefined) { // empty body // create placeholder node const placeholderNode = this._createPlaceholderNode(path.get("body")); this._connectToParents(placeholderNode); this._currentParents = [placeholderNode.id]; firstBodyNode = placeholderNode; } // loop const loopNode = this._createNode(path.get("test")); this._connectToParents(loopNode); // consequent this._currentParents = [loopNode.id]; this._edgeType = cfg_1.EdgeType.CONDITIONAL_TRUE; const consequent = this._createPlaceholderNode(path.get("test")); // bit of a hack to use the test this._connectToParents(consequent); // the back edge this._edges.push(this._createEdge(consequent, firstBodyNode, cfg_1.EdgeType.BACK_EDGE)); // false this._currentParents = [loopNode.id]; this._edgeType = cfg_1.EdgeType.CONDITIONAL_FALSE; const alternate = this._createPlaceholderNode(path); this._connectToParents(alternate); // exit this._currentParents = [alternate.id]; const loopExit = this._createPlaceholderNode(path, true); // connect all break nodes to loop exit this._currentParents.push(...this._regularBreakNodesStack.pop()); this._connectToParents(loopExit); this._currentParents = [loopExit.id]; // connect all continue nodes to test for (const continueNode of this._regularContinueNodesStack.pop()) { this._edges.push(this._createEdge(this._nodes.get(continueNode), loopNode, cfg_1.EdgeType.BACK_EDGE)); } path.skip(); }; this.WhileStatement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering WhileStatement at ${this._getNodeId(path)}`); const whileNode = this._createNode(path); this._connectToParents(whileNode); this._currentParents = [whileNode.id]; this._regularBreakNodesStack.push(new Set()); this._regularContinueNodesStack.push(new Set()); // loop const loopNode = this._createNode(path.get("test")); this._connectToParents(loopNode); // true body this._currentParents = [loopNode.id]; this._edgeType = cfg_1.EdgeType.CONDITIONAL_TRUE; const beforeSize = this._nodes.size; path.get("body").visit(); // check if something was created if (beforeSize === this._nodes.size) { // empty body // create placeholder node const placeholderNode = this._createPlaceholderNode(path.get("body")); this._connectToParents(placeholderNode); this._currentParents = [placeholderNode.id]; } // the back edge this._edgeType = cfg_1.EdgeType.BACK_EDGE; this._connectToParents(loopNode); // TODO should be label back edge too // false this._currentParents = [loopNode.id]; this._edgeType = cfg_1.EdgeType.CONDITIONAL_FALSE; const alternate = this._createPlaceholderNode(path); this._connectToParents(alternate); // TODO should be label back edge too // exit this._currentParents = [alternate.id]; const loopExit = this._createPlaceholderNode(path, true); // connect all break nodes to loop exit this._currentParents.push(...this._regularBreakNodesStack.pop()); this._connectToParents(loopExit); this._currentParents = [loopExit.id]; // connect all continue nodes to test entry for (const continueNode of this._regularContinueNodesStack.pop()) { this._edges.push(this._createEdge(this._nodes.get(continueNode), loopNode, cfg_1.EdgeType.BACK_EDGE)); } path.skip(); }; this.ForStatement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering ForStatement at ${this._getNodeId(path)}`); const forNode = this._createNode(path); this._connectToParents(forNode); this._currentParents = [forNode.id]; this._regularBreakNodesStack.push(new Set()); this._regularContinueNodesStack.push(new Set()); // init if (path.has("init")) { const init = path.get("init"); // stupid hack because the variable declaration of an init is not registered correctly? if (init.isVariableDeclaration()) { const node = this._createNode(init.get("declarations")[0].get("init")); this._connectToParents(node); this._currentParents = [node.id]; } else { init.visit(); } } // test let testNode; if (path.has("test")) { testNode = this._createNode(path.get("test")); this._connectToParents(testNode); this._currentParents = [testNode.id]; // true this._edgeType = cfg_1.EdgeType.CONDITIONAL_TRUE; } // body let beforeSize = this._nodes.size; path.get("body").visit(); // check if something was created if (beforeSize === this._nodes.size) { // empty body // create placeholder node const placeholderNode = this._createPlaceholderNode(path.get("body")); this._connectToParents(placeholderNode); this._currentParents = [placeholderNode.id]; } // update if (path.has("update")) { beforeSize = this._nodesList.length; path.get("update").visit(); if (beforeSize === this._nodesList.length) { throw new Error(`No node was added for the update part of the for loop,`); } } // connect to test if (path.has("test")) { this._edgeType = cfg_1.EdgeType.BACK_EDGE; this._connectToParents(testNode); // false this._currentParents = [testNode.id]; this._edgeType = cfg_1.EdgeType.CONDITIONAL_FALSE; const alternate = this._createPlaceholderNode(path); this._connectToParents(alternate); this._currentParents = [alternate.id]; } else { this._currentParents = []; } // exit const loopExit = this._createPlaceholderNode(path, true); // connect all break nodes to loop exit this._currentParents.push(...this._regularBreakNodesStack.pop()); this._connectToParents(loopExit); this._currentParents = [loopExit.id]; // connect all continue nodes to test for (const continueNode of this._regularContinueNodesStack.pop()) { this._edges.push(this._createEdge(this._nodes.get(continueNode), testNode, cfg_1.EdgeType.BACK_EDGE)); } path.skip(); }; this.ForInStatement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering ForInStatement at ${this._getNodeId(path)}`); const forInNode = this._createNode(path); this._connectToParents(forInNode); this._currentParents = [forInNode.id]; this._regularBreakNodesStack.push(new Set()); this._regularContinueNodesStack.push(new Set()); if (!path.has("left")) { // unsupported throw new Error(`ForInStatement left not implemented at ${this._getNodeId(path)}`); } if (!path.has("right")) { // unsupported throw new Error(`ForInStatement right not implemented at ${this._getNodeId(path)}`); } // left path.get("left").visit(); // test does not exist so we create placeholder? const testNode = this._createPlaceholderNode(path.get("left")); // stupid hack but we cannot have the placeholder twice this._connectToParents(testNode); this._currentParents = [testNode.id]; // true // body this._edgeType = cfg_1.EdgeType.CONDITIONAL_TRUE; const beforeSize = this._nodes.size; path.get("body").visit(); // check if something was created if (beforeSize === this._nodes.size) { // empty body // create placeholder node const placeholderNode = this._createPlaceholderNode(path.get("body")); this._connectToParents(placeholderNode); this._currentParents = [placeholderNode.id]; } // connect to test this._edgeType = cfg_1.EdgeType.BACK_EDGE; this._connectToParents(testNode); // false this._currentParents = [testNode.id]; this._edgeType = cfg_1.EdgeType.CONDITIONAL_FALSE; const alternate = this._createPlaceholderNode(path); this._connectToParents(alternate); // exit this._currentParents = [alternate.id]; const loopExit = this._createPlaceholderNode(path, true); // connect all break nodes to loop exit this._currentParents.push(...this._regularBreakNodesStack.pop()); this._connectToParents(loopExit); this._currentParents = [loopExit.id]; // connect all continue nodes to test for (const continueNode of this._regularContinueNodesStack.pop()) { this._edges.push(this._createEdge(this._nodes.get(continueNode), testNode, cfg_1.EdgeType.BACK_EDGE)); } path.skip(); }; this.ForOfStatement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering ForOfStatement at ${this._getNodeId(path)}`); const forOfNode = this._createNode(path); this._connectToParents(forOfNode); this._currentParents = [forOfNode.id]; this._regularBreakNodesStack.push(new Set()); this._regularContinueNodesStack.push(new Set()); if (!path.has("left")) { // unsupported throw new Error(`ForOfStatement left not implemented at ${this._getNodeId(path)}`); } if (!path.has("right")) { // unsupported throw new Error(`ForOfStatement right not implemented at ${this._getNodeId(path)}`); } // left path.get("left").visit(); // test does not exist so we create placeholder? const testNode = this._createPlaceholderNode(path.get("left")); // stupid hack but we cannot have the placeholder twice this._connectToParents(testNode); this._currentParents = [testNode.id]; // true // body this._edgeType = cfg_1.EdgeType.CONDITIONAL_TRUE; const beforeSize = this._nodes.size; path.get("body").visit(); // check if something was created if (beforeSize === this._nodes.size) { // empty body // create placeholder node const placeholderNode = this._createPlaceholderNode(path.get("body")); this._connectToParents(placeholderNode); this._currentParents = [placeholderNode.id]; } // connect to test this._edgeType = cfg_1.EdgeType.BACK_EDGE; this._connectToParents(testNode); // false this._currentParents = [testNode.id]; this._edgeType = cfg_1.EdgeType.CONDITIONAL_FALSE; const alternate = this._createPlaceholderNode(path); this._connectToParents(alternate); // exit this._currentParents = [alternate.id]; const loopExit = this._createPlaceholderNode(path, true); // connect all break nodes to loop exit this._currentParents.push(...this._regularBreakNodesStack.pop()); this._connectToParents(loopExit); this._currentParents = [loopExit.id]; // connect all continue nodes to loop entry for (const continueNode of this._regularContinueNodesStack.pop()) { this._edges.push(this._createEdge(this._nodes.get(continueNode), testNode, cfg_1.EdgeType.BACK_EDGE)); } path.skip(); }; this.SwitchStatement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering SwitchStatement at ${this._getNodeId(path)}`); this._regularBreakNodesStack.push(new Set()); const switchNode = this._createNode(path); this._connectToParents(switchNode); this._currentParents = [switchNode.id]; const testNode = this._createNode(path.get("discriminant")); this._connectToParents(testNode); this._currentParents = [testNode.id]; let fallThrough = []; for (const caseNode of path.get("cases")) { if (caseNode.has("test")) { // test const caseTestNode = this._createNode(caseNode.get("test")); this._connectToParents(caseTestNode); this._currentParents = [caseTestNode.id]; // consequent this._edgeType = cfg_1.EdgeType.CONDITIONAL_TRUE; const consequentNode = this._createNode(caseNode); this._connectToParents(consequentNode); this._currentParents = [consequentNode.id, ...fallThrough]; if (caseNode.get("consequent").length > 0) { for (const consequentNode of caseNode.get("consequent")) { consequentNode.visit(); } } const trueParents = this._currentParents; // if there is a break these should be empty fallThrough = [...trueParents]; // fall through // alternate // placeholder this._edgeType = cfg_1.EdgeType.CONDITIONAL_FALSE; this._currentParents = [caseTestNode.id]; const alternateNode = this._createPlaceholderNode(caseNode); this._connectToParents(alternateNode); this._currentParents = [alternateNode.id]; // normal } else { // default if (caseNode.get("consequent").length === 0) { // empty body // create placeholder node const placeholderNode = this._createPlaceholderNode(caseNode); this._connectToParents(placeholderNode); this._currentParents = [placeholderNode.id]; } else { for (const consequentNode of caseNode.get("consequent")) { consequentNode.visit(); } } } } // exit const switchExit = this._createPlaceholderNode(path, true); this._currentParents.push( // connect fall through nodes to switch exit ...fallThrough, // connect all break nodes to switch exit ...this._regularBreakNodesStack.pop()); this._connectToParents(switchExit); this._currentParents = [switchExit.id]; path.skip(); }; // terminating statements // these statements are the end of a path // so they don't have any children // which is why we empty the parents list instead of adding the node to it this.BreakStatement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering BreakStatement at ${this._getNodeId(path)}`); const node = this._createNode(path); this._connectToParents(node); if (path.has("label")) { // labeled break node const label = path.get("label").node.name; if (!this._labeledBreakNodes.has(label)) { throw new Error(`Label ${label} does not exist for break node at ${this._getNodeId(path)}`); } this._labeledBreakNodes.get(label).add(node.id); } else { // regular break node this._getBreakNodes().add(node.id); } this._currentParents = []; path.skip(); }; this.ContinueStatement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering ContinueStatement at ${this._getNodeId(path)}`); const node = this._createNode(path); this._connectToParents(node); if (path.has("label")) { // labeled continue node const label = path.get("label").node.name; if (!this._labeledContinueNodes.has(label)) { throw new Error(`Label ${label} does not exist for continue node at ${this._getNodeId(path)}`); } this._labeledContinueNodes.get(label).add(node.id); } else { // regular continue node this._getContinueNodes().add(node.id); } this._currentParents = []; path.skip(); }; this.ReturnStatement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering ReturnStatement at ${this._getNodeId(path)}`); const node = this._createNode(path); this._connectToParents(node); this._currentParents = [node.id]; if (path.has("argument")) { path.get("argument").visit(); } for (const nodeId of this._currentParents) { this._returnNodes.add(nodeId); } this._currentParents = []; path.skip(); }; this.ThrowStatement = (path) => { ControlFlowGraphVisitor.LOGGER.debug(`Entering ThrowStatement at ${this._getNodeId(path)}`); const node = this._createNode(path); this._connectToParents(node); this._throwNodes.add(node.id); this._currentParents = []; path.skip(); }; ControlFlowGraphVisitor.LOGGER = (0, logging_1.getLogger)("ControlFlowGraphVisitor"); this._nodesList = []; this._nodes = new Map(); this._edges = []; this._labeledBreakNodes = new Map(); this._labeledContinueNodes = new Map(); this._regularBreakNodesStack = []; this._regularContinueNodesStack = []; this._returnNodes = new Set(); this._throwNodes = new Set(); this._functions = []; this._currentParents = []; this._edgeType = cfg_1.EdgeType.NORMAL; const entry = new cfg_1.Node("ENTRY", cfg_1.NodeType.ENTRY, "ENTRY", [], {}); const successExit = new cfg_1.Node("SUCCESS_EXIT", cfg_1.NodeType.EXIT, "EXIT", [], {}); const errorExit = new cfg_1.Node("ERROR_EXIT", cfg_1.NodeType.EXIT, "EXIT", [], {}); this._nodes.set(entry.id, entry); this._nodes.set(successExit.id, successExit); this._nodes.set(errorExit.id, errorExit); this._nodesList.push(entry, successExit, errorExit); this._currentParents = [entry.id]; } _getBreakNodes() { if (this._regularBreakNodesStack.length === 0) { throw new Error("No break nodes found"); } return this._regularBreakNodesStack[this._regularBreakNodesStack.length - 1]; } _getContinueNodes() { if (this._regularContinueNodesStack.length === 0) { throw new Error("No continue nodes found"); } return this._regularContinueNodesStack[this._regularContinueNodesStack.length - 1]; } _getLocation(path) { return { start: { line: path.node.loc.start.line, column: path.node.loc.start.column, index: path.node.loc.start.index, }, end: { line: path.node.loc.end.line, column: path.node.loc.end.column, index: path.node.loc.end.index, }, }; } _createNode(path) { const id = `${this._getNodeId(path)}`; const node = new cfg_1.Node(id, cfg_1.NodeType.NORMAL, path.node.type, [ { id: id, location: this._getLocation(path), statementAsText: path.toString(), }, ], {}, path.node.type); if (this._nodes.has(id)) { throw new Error(`Node already registered ${id}`); } this._nodes.set(id, node); this._nodesList.push(node); return node; } _getPlaceholderNodeId(path) { if (path.node.loc === undefined) { throw new Error(`Node ${path.type} in file '${this._filePath}' does not have a location`); } const startLine = path.node.loc.start.line; const startColumn = path.node.loc.start .column; const startIndex = path.node.loc.start .index; const endLine = path.node.loc.end.line; const endColumn = path.node.loc.end.column; const endIndex = path.node.loc.end.index; return `${this._filePath}:${startLine}:${startColumn}:::${endLine}:${endColumn}:::${startIndex}:${endIndex}`; } /** * Create a placeholder node for a node that is not in the AST, but is used in the CFG. * Uses the end location of the parent node as the start and end location of the placeholder node. * @param path * @returns */ _createPlaceholderNode(path, double = false) { let id = `placeholder:::${this._getPlaceholderNodeId(path)}`; if (double) { id = "placeholder:::" + id; } const location = this._getLocation(path); const node = new cfg_1.Node(id, cfg_1.NodeType.NORMAL, path.node.type, [ { id: id, location: { start: { line: location.start.line, column: location.start.column, index: location.start.index, }, end: { line: location.end.line, column: location.end.column, index: location.end.index, }, }, statementAsText: path.toString(), }, ], {}, path.node.type); if (this._nodes.has(id)) { throw new Error(`Node already registered ${id}`); } this._nodes.set(id, node); this._nodesList.push(node); return node; } _createEdge(source, target, edgeType, label = "") { return new cfg_1.Edge(`${source.id}->${target.id}`, edgeType, label, source.id, target.id, "description"); } /** * Connects the current parents to the given node * It uses the current edge type and resets it back to normal afterwards * * @param node */ _connectToParents(node) { // it is actually possible that there are no parents for (const parent of this._currentParents) { this._edges.push(this._createEdge(this._nodes.get(parent), node, this._edgeType)); this._edgeType = cfg_1.EdgeType.NORMAL; } } } exports.ControlFlowGraphVisitor = ControlFlowGraphVisitor; //# sourceMappingURL=ControlFlowGraphVisitor.js.map