@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
595 lines • 27.3 kB
JavaScript
"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