@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
100 lines • 4.08 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.addNonDefaultExitPoints = addNonDefaultExitPoints;
exports.initializeCleanDataflowInformation = initializeCleanDataflowInformation;
exports.happensInEveryBranch = happensInEveryBranch;
exports.alwaysExits = alwaysExits;
exports.filterOutLoopExitPoints = filterOutLoopExitPoints;
exports.diffControlDependency = diffControlDependency;
exports.diffControlDependencies = diffControlDependencies;
const graph_1 = require("./graph/graph");
/**
* Adds all non-default exit points to the existing list.
*/
function addNonDefaultExitPoints(existing, add) {
existing.push(...add.filter(({ type }) => type !== 0 /* ExitPointType.Default */));
}
/**
* Initializes an empty {@link DataflowInformation} object with the given entry point and data.
* This is to be used as a "starting point" when processing leaf nodes during the dataflow extraction.
*
* @see {@link DataflowInformation}
*/
function initializeCleanDataflowInformation(entryPoint, data) {
return {
unknownReferences: [],
in: [],
out: [],
environment: data.environment,
graph: new graph_1.DataflowGraph(data.completeAst.idMap),
entryPoint,
exitPoints: [{ nodeId: entryPoint, type: 0 /* ExitPointType.Default */, controlDependencies: undefined }]
};
}
/**
* Checks whether the given control dependencies are exhaustive (i.e. if for every control dependency on a boolean,
* the list contains a dependency on the `true` and on the `false` case).
*/
function happensInEveryBranch(controlDependencies) {
if (controlDependencies === undefined) {
/* the cds are unconstrained */
return true;
}
else if (controlDependencies.length === 0) {
/* this happens only when we have no idea and require more analysis */
return false;
}
const trues = [];
const falseSet = new Set();
for (const { id, when } of controlDependencies) {
if (when) {
trues.push(id);
}
else {
falseSet.add(id);
}
}
return trues.every(id => falseSet.has(id));
}
/**
* Checks whether the given dataflow information always exits (i.e., if there is a non-default exit point in every branch).
* @see {@link ExitPoint} - for the different types of exit points
*/
function alwaysExits(data) {
return data.exitPoints?.some(e => e.type !== 0 /* ExitPointType.Default */ && happensInEveryBranch(e.controlDependencies)) ?? false;
}
/**
* Filters out exit points which end their cascade within a loop.
*/
function filterOutLoopExitPoints(exitPoints) {
return exitPoints.filter(({ type }) => type === 1 /* ExitPointType.Return */ || type === 0 /* ExitPointType.Default */);
}
function diffControlDependency(a, b, info) {
if (a === undefined || b === undefined) {
if (a !== b) {
info.report.addComment(`${info.position}Different control dependencies. ${info.leftname}: ${JSON.stringify(a)} vs. ${info.rightname}: ${JSON.stringify(b)}`);
}
return;
}
if (a.id !== b.id) {
info.report.addComment(`${info.position}Different control dependency ids. ${info.leftname}: ${a.id} vs. ${info.rightname}: ${b.id}`);
}
if (a.when !== b.when) {
info.report.addComment(`${info.position}Different control dependency when. ${info.leftname}: ${a.when} vs. ${info.rightname}: ${b.when}`);
}
}
function diffControlDependencies(a, b, info) {
if (a === undefined || b === undefined) {
if (a !== b) {
info.report.addComment(`${info.position}Different control dependencies: ${JSON.stringify(a)} vs. ${JSON.stringify(b)}`);
}
return;
}
if (a.length !== b.length) {
info.report.addComment(`${info.position}Different control dependency lengths: ${a.length} vs. ${b.length}`);
}
for (let i = 0; i < a.length; ++i) {
diffControlDependency(a[i], b[i], { ...info, position: `${info.position}Control dependency at index: ${i}: ` });
}
}
//# sourceMappingURL=info.js.map