UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

595 lines 27.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResolvedCallSuffix = void 0; exports.extractCfg = extractCfg; exports.extractCfgQuick = extractCfgQuick; exports.getCallsInCfg = getCallsInCfg; exports.cfg2quads = cfg2quads; const quads_1 = require("../util/quads"); const r_function_definition_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-definition"); const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call"); const linker_1 = require("../dataflow/internal/linker"); const vertex_1 = require("../dataflow/graph/vertex"); const control_flow_graph_1 = require("./control-flow-graph"); const cfg_simplification_1 = require("./cfg-simplification"); const assert_1 = require("../util/assert"); const stateful_fold_1 = require("../r-bridge/lang-4.x/ast/model/processing/stateful-fold"); const model_1 = require("../r-bridge/lang-4.x/ast/model/model"); const built_in_proc_name_1 = require("../dataflow/environments/built-in-proc-name"); const cfgFolds = { down: (n, down) => { if (r_function_definition_1.RFunctionDefinition.is(n)) { return [down[0], true]; } else if (model_1.RLoopConstructs.is(n)) { return [true, down[1]]; } return down; }, foldNumber: cfgLeaf(control_flow_graph_1.CfgVertexType.Expression), foldString: cfgLeaf(control_flow_graph_1.CfgVertexType.Expression), foldLogical: cfgLeaf(control_flow_graph_1.CfgVertexType.Expression), foldSymbol: cfgLeaf(control_flow_graph_1.CfgVertexType.Expression), foldAccess: cfgAccess, foldBinaryOp: cfgBinaryOp, foldPipe: cfgBinaryOp, foldUnaryOp: cfgUnaryOp, other: { foldComment: cfgIgnore, foldLineDirective: cfgIgnore }, loop: { foldFor: cfgFor, foldRepeat: cfgRepeat, foldWhile: cfgWhile, foldBreak: cfgBreak, foldNext: cfgNext }, foldIfThenElse: cfgIfThenElse, foldExprList: cfgExprList, functions: { foldFunctionDefinition: cfgFunctionDefinition, foldFunctionCall: cfgFunctionCall, foldParameter: cfgArgumentOrParameter, foldArgument: cfgArgumentOrParameter } }; const ignoreFunctDefCfgFolds = { ...cfgFolds, functions: { ...cfgFolds.functions, foldFunctionDefinition: cfgLeaf(control_flow_graph_1.CfgVertexType.Expression) } }; function dataflowCfgFolds(dataflowGraph) { const newFolds = { ...cfgFolds, }; newFolds.functions = { ...cfgFolds.functions, foldFunctionCall: cfgFunctionCallWithDataflow(dataflowGraph, newFolds) }; return newFolds; } /** * Given a normalized AST, this approximates the control flow graph of the program. * This view is different from the computation of the dataflow graph and may differ, * especially because it focuses on intra-procedural analysis. * @param ast - the normalized AST * @param ctx - the flowR context * @param graph - additional dataflow facts to consider by the control flow extraction * @param simplifications - a list of simplification passes to apply to the control flow graph * @param ignoreFunctDefs - whether function definition vertices should be ignored * @see {@link extractCfgQuick} - for a simplified version of this function */ function extractCfg(ast, ctx, graph, simplifications, ignoreFunctDefs) { const folds = ignoreFunctDefs ? ignoreFunctDefCfgFolds : (graph ? dataflowCfgFolds(graph) : cfgFolds); return (0, cfg_simplification_1.simplifyControlFlowInformation)(cfgFoldProject(ast.ast, folds), { ast, dfg: graph, ctx }, simplifications); } /** * A version of {@link extractCfg} that is much quicker and does not apply any simplifications or dataflow information. */ function extractCfgQuick(ast) { return cfgFoldProject(ast.ast, cfgFolds); } /** * Extracts all function call vertices from the given control flow information and dataflow graph. */ function getCallsInCfg(cfg, graph) { const calls = new Map(); for (const vertexId of cfg.graph.vertices().keys()) { const vertex = graph.getVertex(vertexId); if ((0, vertex_1.isFunctionCallVertex)(vertex)) { calls.set(vertexId, vertex); } } return calls; } function cfgFoldProject(proj, folds) { if (proj.files.length === 0) { return (0, control_flow_graph_1.emptyControlFlowInformation)(); } else if (proj.files.length === 1) { return (0, stateful_fold_1.foldAstStateful)(proj.files[0].root, [false, false], folds); } /* for many files, it is too expensive to keep all asts at once, hence we create and merge them incrementally */ let exitPoints; let finalGraph; let firstEntryPoints; let breaks; let nexts; let returns; { const firstInfo = (0, stateful_fold_1.foldAstStateful)(proj.files[0].root, [false, false], folds); exitPoints = firstInfo.exitPoints; finalGraph = firstInfo.graph; firstEntryPoints = firstInfo.entryPoints; breaks = firstInfo.breaks; nexts = firstInfo.nexts; returns = firstInfo.returns; } for (let i = 1; i < proj.files.length; i++) { const nextInfo = (0, stateful_fold_1.foldAstStateful)(proj.files[i].root, [false, false], folds); finalGraph.mergeWith(nextInfo.graph); for (const exitPoint of exitPoints) { for (const entryPoint of nextInfo.entryPoints) { finalGraph.addEdge(entryPoint, exitPoint, control_flow_graph_1.CfgEdge.makeFd()); } } exitPoints = nextInfo.exitPoints; breaks.push(...nextInfo.breaks); nexts.push(...nextInfo.nexts); returns.push(...nextInfo.returns); } return { breaks, nexts, returns, exitPoints, entryPoints: firstEntryPoints, graph: finalGraph }; } function cfgLeaf(type) { return type === control_flow_graph_1.CfgVertexType.Expression ? ({ info: { id } }) => { return { graph: new control_flow_graph_1.ControlFlowGraph().addVertex(control_flow_graph_1.CfgVertex.makeExpression(id)), breaks: [], nexts: [], returns: [], exitPoints: [id], entryPoints: [id] }; } : ({ info: { id } }) => { return { graph: new control_flow_graph_1.ControlFlowGraph().addVertex(control_flow_graph_1.CfgVertex.makeStatement(id)), breaks: [], nexts: [], returns: [], exitPoints: [id], entryPoints: [id] }; }; } const cfgLeafStatement = cfgLeaf(control_flow_graph_1.CfgVertexType.Statement); function cfgBreak(leaf, down) { if (!down[0]) { return cfgLeafStatement(leaf); } return { ...cfgLeafStatement(leaf), breaks: [leaf.info.id], exitPoints: [] }; } function cfgNext(leaf, down) { if (!down[0]) { return cfgLeafStatement(leaf); } return { ...cfgLeafStatement(leaf), nexts: [leaf.info.id], exitPoints: [] }; } function cfgIgnore(_leaf) { return { graph: new control_flow_graph_1.ControlFlowGraph(), breaks: [], nexts: [], returns: [], exitPoints: [], entryPoints: [] }; } function identifyMayStatementType(node) { return node.info.role === "el-c" /* RoleInParent.ExpressionListChild */ ? control_flow_graph_1.CfgVertexType.Statement : control_flow_graph_1.CfgVertexType.Expression; } function cfgIfThenElse(ifNode, condition, then, otherwise) { const ifId = ifNode.info.id; const graph = new control_flow_graph_1.ControlFlowGraph(); graph.addVertex(control_flow_graph_1.CfgVertex.makeExprOrStm(ifId, identifyMayStatementType(ifNode), { mid: condition.exitPoints, end: [control_flow_graph_1.CfgVertex.toExitId(ifId)] })); graph.addVertex(control_flow_graph_1.CfgVertex.makeExitMarker(ifId)); graph.mergeWith(condition.graph); graph.mergeWith(then.graph); if (otherwise) { graph.mergeWith(otherwise.graph); } const cdTrue = control_flow_graph_1.CfgEdge.makeCdTrue(ifId); const cdFalse = control_flow_graph_1.CfgEdge.makeCdFalse(ifId); for (const e of condition.exitPoints) { for (const entryPoint of then.entryPoints) { graph.addEdge(entryPoint, e, cdTrue); } for (const entryPoint of otherwise?.entryPoints ?? []) { graph.addEdge(entryPoint, e, cdFalse); } } for (const entryPoint of condition.entryPoints) { graph.addEdge(entryPoint, ifId, control_flow_graph_1.CfgEdge.makeFd()); } for (const exits of [then.exitPoints, otherwise?.exitPoints ?? []]) { for (const exit of exits) { graph.addEdge(control_flow_graph_1.CfgVertex.toExitId(ifId), exit, control_flow_graph_1.CfgEdge.makeFd()); } } if (!otherwise) { for (const e of condition.exitPoints) { graph.addEdge(control_flow_graph_1.CfgVertex.toExitId(ifId), e, control_flow_graph_1.CfgEdge.makeCdFalse(ifId)); } } return { graph, breaks: then.breaks.concat(otherwise?.breaks ?? []), nexts: then.nexts.concat(otherwise?.nexts ?? []), returns: then.returns.concat(otherwise?.returns ?? []), exitPoints: [control_flow_graph_1.CfgVertex.toExitId(ifId)], entryPoints: [ifId] }; } function cfgRepeat(repeat, body) { const graph = body.graph; const rid = repeat.info.id; graph.addVertex(control_flow_graph_1.CfgVertex.makeExprOrStm(rid, identifyMayStatementType(repeat), { end: [control_flow_graph_1.CfgVertex.toExitId(rid)] })); graph.addVertex(control_flow_graph_1.CfgVertex.makeExitMarker(rid)); for (const entryPoint of body.entryPoints) { graph.addEdge(entryPoint, rid, control_flow_graph_1.CfgEdge.makeFd()); } // loops automatically for (const nexts of [body.nexts, body.exitPoints]) { for (const next of nexts) { graph.addEdge(rid, next, control_flow_graph_1.CfgEdge.makeFd()); } } for (const breakPoint of body.breaks) { graph.addEdge(control_flow_graph_1.CfgVertex.toExitId(rid), breakPoint, control_flow_graph_1.CfgEdge.makeFd()); } return { graph, breaks: [], nexts: [], returns: body.returns, exitPoints: [control_flow_graph_1.CfgVertex.toExitId(rid)], entryPoints: [rid] }; } function cfgWhile(whileLoop, condition, body) { const whileId = whileLoop.info.id; const graph = condition.graph; graph.addVertex(control_flow_graph_1.CfgVertex.makeExprOrStm(whileId, identifyMayStatementType(whileLoop), { mid: condition.exitPoints, end: [control_flow_graph_1.CfgVertex.toExitId(whileId)] })); graph.addVertex(control_flow_graph_1.CfgVertex.makeExitMarker(whileId)); graph.mergeWith(body.graph); for (const entry of condition.entryPoints) { graph.addEdge(entry, whileId, control_flow_graph_1.CfgEdge.makeFd()); } for (const e of condition.exitPoints) { for (const entry of body.entryPoints) { graph.addEdge(entry, e, control_flow_graph_1.CfgEdge.makeCdTrue(whileId)); } } for (const nexts of [body.nexts, body.exitPoints]) { for (const next of nexts) { graph.addEdge(whileId, next, control_flow_graph_1.CfgEdge.makeFd()); } } for (const breakPoint of body.breaks) { graph.addEdge(control_flow_graph_1.CfgVertex.toExitId(whileId), breakPoint, control_flow_graph_1.CfgEdge.makeFd()); } // while can break on the condition as well for (const e of condition.exitPoints) { graph.addEdge(control_flow_graph_1.CfgVertex.toExitId(whileId), e, control_flow_graph_1.CfgEdge.makeCdFalse(whileId)); } return { graph, breaks: [], nexts: [], returns: body.returns, exitPoints: [control_flow_graph_1.CfgVertex.toExitId(whileId)], entryPoints: [whileId] }; } function cfgFor(forLoop, variable, vector, body) { const forLoopId = forLoop.info.id; const graph = variable.graph; graph.addVertex(control_flow_graph_1.CfgVertex.makeExprOrStm(forLoopId, identifyMayStatementType(forLoop), { mid: variable.exitPoints, end: [control_flow_graph_1.CfgVertex.toExitId(forLoopId)] })); graph.mergeWith(vector.graph); graph.mergeWith(body.graph); for (const entry of vector.entryPoints) { graph.addEdge(entry, forLoopId, control_flow_graph_1.CfgEdge.makeFd()); } for (const exit of vector.exitPoints) { for (const entry of variable.entryPoints) { graph.addEdge(entry, exit, control_flow_graph_1.CfgEdge.makeFd()); } } for (const e of variable.exitPoints) { for (const entry of body.entryPoints) { graph.addEdge(entry, e, control_flow_graph_1.CfgEdge.makeCdTrue(forLoopId)); } } for (const points of [body.nexts, body.exitPoints]) { for (const next of points) { graph.addEdge(forLoopId, next, control_flow_graph_1.CfgEdge.makeFd()); } } for (const breakPoint of body.breaks) { graph.addEdge(control_flow_graph_1.CfgVertex.toExitId(forLoopId), breakPoint, control_flow_graph_1.CfgEdge.makeFd()); } graph.addVertex(control_flow_graph_1.CfgVertex.makeExitMarker(forLoopId)); for (const e of variable.exitPoints) { graph.addEdge(control_flow_graph_1.CfgVertex.toExitId(forLoopId), e, control_flow_graph_1.CfgEdge.makeCdFalse(forLoopId)); } return { graph, breaks: [], nexts: [], returns: body.returns, exitPoints: [control_flow_graph_1.CfgVertex.toExitId(forLoopId)], entryPoints: [forLoopId] }; } function cfgFunctionDefinition(fn, params, body) { const fnId = fn.info.id; const graph = new control_flow_graph_1.ControlFlowGraph(); let paramExits = params.flatMap(e => e.exitPoints); const children = [...paramExits, control_flow_graph_1.CfgVertex.toExitId(fnId)]; graph.addVertex(control_flow_graph_1.CfgVertex.makeExitMarker(fnId), false); graph.addVertex(control_flow_graph_1.CfgVertex.makeExprOrStm(fnId, identifyMayStatementType(fn), { children, mid: paramExits, end: [control_flow_graph_1.CfgVertex.toExitId(fnId)] })); graph.mergeWith(body.graph, true); for (const r of body.graph.rootIds()) { children.push(r); } for (const param of params) { graph.mergeWith(param.graph, true); for (const r of param.graph.rootIds()) { children.push(r); } for (const entry of param.entryPoints) { graph.addEdge(entry, fnId, control_flow_graph_1.CfgEdge.makeFd()); } } if (paramExits.length === 0) { paramExits = [fnId]; } for (const e of paramExits) { for (const entry of body.entryPoints) { graph.addEdge(entry, e, control_flow_graph_1.CfgEdge.makeFd()); } } // breaks and nexts should be illegal but safe is safe, I guess for (const next of body.returns.concat(body.breaks, body.nexts, body.exitPoints)) { graph.addEdge(control_flow_graph_1.CfgVertex.toExitId(fnId), next, control_flow_graph_1.CfgEdge.makeFd()); } return { graph: graph, breaks: [], nexts: [], returns: [], exitPoints: [fnId], entryPoints: [fnId] }; } function cfgFunctionCall(call, name, args, down) { if (call.named && call.functionName.content === 'ifelse' && args.length > 1) { // special built-in handling for ifelse as it is an expression that does not short-circuit return cfgIfThenElse(call, args[0] === r_function_call_1.EmptyArgument ? (0, control_flow_graph_1.emptyControlFlowInformation)() : args[0], args[1] === r_function_call_1.EmptyArgument ? (0, control_flow_graph_1.emptyControlFlowInformation)() : args[1], args[2] === r_function_call_1.EmptyArgument ? (0, control_flow_graph_1.emptyControlFlowInformation)() : args[2]); } const callId = call.info.id; const graph = name.graph; const info = { graph, breaks: Array.from(name.breaks), nexts: Array.from(name.nexts), returns: Array.from(name.returns), exitPoints: [control_flow_graph_1.CfgVertex.toExitId(callId)], entryPoints: [callId] }; graph.addVertex(control_flow_graph_1.CfgVertex.makeExprOrStm(callId, identifyMayStatementType(call), { mid: name.exitPoints, end: [control_flow_graph_1.CfgVertex.toExitId(callId)] })); for (const entryPoint of name.entryPoints) { graph.addEdge(entryPoint, callId, control_flow_graph_1.CfgEdge.makeFd()); } graph.addVertex(control_flow_graph_1.CfgVertex.makeExitMarker(callId)); let lastArgExits = name.exitPoints; for (const arg of args) { if (arg === r_function_call_1.EmptyArgument) { continue; } graph.mergeWith(arg.graph); info.breaks = info.breaks.concat(arg.breaks); info.nexts = info.nexts.concat(arg.nexts); info.returns = info.returns.concat(arg.returns); for (const entry of arg.entryPoints) { for (const exit of lastArgExits) { graph.addEdge(entry, exit, control_flow_graph_1.CfgEdge.makeFd()); } } lastArgExits = arg.exitPoints; } for (const exit of lastArgExits) { graph.addEdge(control_flow_graph_1.CfgVertex.toExitId(callId), exit, control_flow_graph_1.CfgEdge.makeFd()); } if (call.named && call.functionName.content === 'return') { if (down[1]) { info.returns.push(control_flow_graph_1.CfgVertex.toExitId(callId)); info.exitPoints.length = 0; } } // should not contain any breaks, nexts, or returns, (except for the body if something like 'break()') return info; } exports.ResolvedCallSuffix = control_flow_graph_1.CfgVertex.toExitId('-resolved-call'); const OriginToFoldTypeMap = { [built_in_proc_name_1.BuiltInProcName.IfThenElse]: (folds, call, args, down) => { // arguments are in order! return folds.foldIfThenElse(call, // we will have to this more sophisticated if we rewrite the dfg based generation args[0] === r_function_call_1.EmptyArgument ? (0, control_flow_graph_1.emptyControlFlowInformation)() : args[0], args[1] === r_function_call_1.EmptyArgument ? (0, control_flow_graph_1.emptyControlFlowInformation)() : args[1], args[2] === r_function_call_1.EmptyArgument ? (0, control_flow_graph_1.emptyControlFlowInformation)() : args[2], down); } }; function cfgFunctionCallWithDataflow(graph, folds) { return (call, name, args, down) => { const vtx = graph.getVertex(call.info.id); if (vtx?.tag === vertex_1.VertexType.FunctionCall && vtx.onlyBuiltin && vtx.origin.length === 1) { const mayMap = OriginToFoldTypeMap[vtx.origin[0]]; if (mayMap) { return mayMap(folds, call, args, down, vtx); } } const baseCfg = cfgFunctionCall(call, name, args, down); /* try to resolve the call and link the target definitions */ const targets = (0, linker_1.getAllFunctionCallTargets)(call.info.id, graph); const exits = []; const callVertex = baseCfg.graph.getVertex(call.info.id); (0, assert_1.guard)(callVertex !== undefined, 'cfgFunctionCallWithDataflow: call vertex not found'); for (const target of targets) { // we have to filter out non-func-call targets as the call targets contains names and call ids if ((0, vertex_1.isFunctionDefinitionVertex)(graph.getVertex(target))) { const ct = control_flow_graph_1.CfgVertex.getCallTargets(callVertex); if (!ct) { control_flow_graph_1.CfgVertex.setCallTargets(callVertex, new Set([target])); } else { ct.add(target); } exits.push(control_flow_graph_1.CfgVertex.toExitId(target)); } } if (exits.length > 0) { baseCfg.graph.addVertex(control_flow_graph_1.CfgVertex.makeMarker(call.info.id + exports.ResolvedCallSuffix, call.info.id)); for (const col of [baseCfg.exitPoints, exits]) { for (const exit of col) { baseCfg.graph.addEdge(call.info.id + exports.ResolvedCallSuffix, exit, control_flow_graph_1.CfgEdge.makeFd()); } } return { ...baseCfg, exitPoints: [call.info.id + exports.ResolvedCallSuffix] }; } else { return baseCfg; } }; } function cfgArgumentOrParameter(node, name, value) { const graph = new control_flow_graph_1.ControlFlowGraph(); const nodeId = node.info.id; const info = { graph, breaks: [], nexts: [], returns: [], exitPoints: [control_flow_graph_1.CfgVertex.toExitId(nodeId)], entryPoints: [nodeId] }; let currentExitPoints = name?.exitPoints ?? [nodeId]; graph.addVertex(control_flow_graph_1.CfgVertex.makeExpressionWithEnd(nodeId, { mid: currentExitPoints })); if (name) { graph.mergeWith(name.graph); info.breaks = info.breaks.concat(name.breaks); info.nexts = info.nexts.concat(name.nexts); info.returns = info.returns.concat(name.returns); for (const entry of name.entryPoints) { graph.addEdge(entry, nodeId, control_flow_graph_1.CfgEdge.makeFd()); } } if (value) { graph.mergeWith(value.graph); info.breaks = info.breaks.concat(value.breaks); info.nexts = info.nexts.concat(value.nexts); info.returns = info.returns.concat(value.returns); for (const exitPoint of currentExitPoints) { for (const entry of value.entryPoints) { graph.addEdge(entry, exitPoint, control_flow_graph_1.CfgEdge.makeFd()); } } currentExitPoints = value.exitPoints; } graph.addVertex(control_flow_graph_1.CfgVertex.makeExitMarker(nodeId)); for (const exit of currentExitPoints) { graph.addEdge(control_flow_graph_1.CfgVertex.toExitId(nodeId), exit, control_flow_graph_1.CfgEdge.makeFd()); } return info; } function cfgBinaryOp(binOp, lhs, rhs) { const graph = lhs.graph.mergeWith(rhs.graph); const binId = binOp.info.id; const binExit = control_flow_graph_1.CfgVertex.toExitId(binId); const result = { graph, breaks: lhs.breaks.concat(rhs.breaks), nexts: lhs.nexts.concat(rhs.nexts), returns: lhs.returns.concat(rhs.returns), entryPoints: [binId], exitPoints: [binExit] }; graph.addVertex(control_flow_graph_1.CfgVertex.makeExprOrStm(binId, binOp.flavor === 'assignment' ? control_flow_graph_1.CfgVertexType.Statement : control_flow_graph_1.CfgVertexType.Expression, { end: [binExit] })); graph.addVertex(control_flow_graph_1.CfgVertex.makeExitMarker(binId)); const fd = control_flow_graph_1.CfgEdge.makeFd(); for (const exitPoint of lhs.exitPoints) { for (const entryPoint of rhs.entryPoints) { result.graph.addEdge(entryPoint, exitPoint, fd); } } for (const entryPoint of lhs.entryPoints) { graph.addEdge(entryPoint, binId, fd); } for (const exitPoint of rhs.exitPoints) { graph.addEdge(binExit, exitPoint, fd); } return result; } function cfgAccess(access, name, accessors) { const result = { ...name }; const graph = result.graph; const accessId = access.info.id; graph.addVertex(control_flow_graph_1.CfgVertex.makeExpressionWithEnd(accessId, { mid: name.exitPoints })); result.entryPoints = [accessId]; for (const entry of name.entryPoints) { graph.addEdge(entry, accessId, control_flow_graph_1.CfgEdge.makeFd()); } result.exitPoints = name.exitPoints; for (const accessor of accessors) { if (accessor === r_function_call_1.EmptyArgument) { continue; } graph.mergeWith(accessor.graph); for (const exitPoint of result.exitPoints) { for (const entry of accessor.entryPoints) { graph.addEdge(entry, exitPoint, control_flow_graph_1.CfgEdge.makeFd()); } } result.exitPoints = accessor.exitPoints; result.breaks = result.breaks.concat(accessor.breaks); result.nexts = result.nexts.concat(accessor.nexts); result.returns = result.returns.concat(accessor.returns); } for (const exitPoint of result.exitPoints) { graph.addEdge(control_flow_graph_1.CfgVertex.toExitId(accessId), exitPoint, control_flow_graph_1.CfgEdge.makeFd()); } graph.addVertex(control_flow_graph_1.CfgVertex.makeExitMarker(accessId)); result.exitPoints = [control_flow_graph_1.CfgVertex.toExitId(accessId)]; return result; } function cfgUnaryOp(unary, operand) { const graph = operand.graph; const unaryId = unary.info.id; graph.addVertex(control_flow_graph_1.CfgVertex.makeMarker(unaryId, unaryId)); const fd = control_flow_graph_1.CfgEdge.makeFd(); for (const entry of operand.exitPoints) { graph.addEdge(unaryId, entry, fd); } return { ...operand, graph, exitPoints: [unaryId] }; } function cfgExprList(node, _grouping, expressions) { const nodeId = node.info.id; const result = { graph: new control_flow_graph_1.ControlFlowGraph(), breaks: [], nexts: [], returns: [], exitPoints: [nodeId], entryPoints: [nodeId] }; const vtx = control_flow_graph_1.CfgVertex.makeExpression(nodeId); result.graph.addVertex(vtx); const fd = control_flow_graph_1.CfgEdge.makeFd(); for (const expression of expressions) { for (const previousExitPoint of result.exitPoints) { for (const entryPoint of expression.entryPoints) { result.graph.addEdge(entryPoint, previousExitPoint, fd); } } result.graph.mergeWith(expression.graph); result.breaks.push(...expression.breaks); result.nexts.push(...expression.nexts); result.returns.push(...expression.returns); result.exitPoints = expression.exitPoints; } const exitId = control_flow_graph_1.CfgVertex.toExitId(nodeId); if (result.exitPoints.length > 0) { result.graph.addVertex(control_flow_graph_1.CfgVertex.makeExitMarker(nodeId)); control_flow_graph_1.CfgVertex.setEnd(vtx, [exitId]); } for (const exit of result.exitPoints) { result.graph.addEdge(exitId, exit, fd); } result.exitPoints = result.exitPoints.length > 0 ? [exitId] : []; return result; } /** * Convert a cfg to RDF quads. * @see {@link df2quads} * @see {@link serialize2quads} * @see {@link graph2quads} */ function cfg2quads(cfg, config) { return (0, quads_1.graph2quads)({ rootIds: [...cfg.graph.rootIds()], vertices: [...cfg.graph.vertices().entries()] .map(([id, v]) => ({ id, children: control_flow_graph_1.CfgVertex.getChildren(v) })), edges: [...cfg.graph.edges()].flatMap(([fromId, targets]) => [...targets].map(([toId, info]) => ({ from: fromId, to: toId, type: control_flow_graph_1.CfgEdge.getType(info), when: control_flow_graph_1.CfgEdge.getWhen(info) }))), entryPoints: cfg.entryPoints, exitPoints: cfg.exitPoints, breaks: cfg.breaks, nexts: cfg.nexts, returns: cfg.returns }, config); } //# sourceMappingURL=extract-cfg.js.map