@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
85 lines • 5.14 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.processWhileLoop = processWhileLoop;
const info_1 = require("../../../../../info");
const linker_1 = require("../../../../linker");
const known_call_handling_1 = require("../known-call-handling");
const assert_1 = require("../../../../../../util/assert");
const unpack_argument_1 = require("../argument/unpack-argument");
const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const logger_1 = require("../../../../../logger");
const environment_1 = require("../../../../../environments/environment");
const edge_1 = require("../../../../../graph/edge");
const identifier_1 = require("../../../../../environments/identifier");
const general_1 = require("../../../../../eval/values/general");
const alias_tracking_1 = require("../../../../../eval/resolve/alias-tracking");
function processWhileLoop(name, args, rootId, data) {
if (args.length !== 2 || args[1] === r_function_call_1.EmptyArgument) {
logger_1.dataflowLogger.warn(`While-Loop ${name.content} does not have 2 arguments, skipping`);
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information;
}
const unpackedArgs = args.map(e => (0, unpack_argument_1.unpackArgument)(e));
if (unpackedArgs.some(assert_1.isUndefined)) {
logger_1.dataflowLogger.warn(`While-Loop ${name.content} has empty arguments in ${JSON.stringify(args)}, skipping`);
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information;
}
// we should defer this to the abstract interpretation
const values = (0, alias_tracking_1.resolveIdToValue)(unpackedArgs[0]?.info.id, { environment: data.environment, idMap: data.completeAst.idMap });
const conditionIsAlwaysFalse = (0, general_1.valueSetGuard)(values)?.elements.every(d => d.type === 'logical' && d.value === false) ?? false;
//We don't care about the body if it never executes
if (conditionIsAlwaysFalse) {
unpackedArgs.pop();
}
/* we inject the cf-dependency of the while-loop after the condition */
const { information, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({
name,
args: unpackedArgs,
rootId,
data,
markAsNSE: [1],
patchData: (d, i) => {
if (i === 1) {
return { ...d, controlDependencies: [...d.controlDependencies ?? [], { id: name.info.id, when: true }] };
}
return d;
}, origin: 'builtin:while-loop'
});
const [condition, body] = processedArguments;
// If the condition is always false, we don't include the body
if (condition !== undefined && conditionIsAlwaysFalse) {
information.graph.addEdge(name.info.id, condition.entryPoint, edge_1.EdgeType.Reads);
return {
unknownReferences: [],
in: [{ nodeId: name.info.id, name: name.lexeme, controlDependencies: data.controlDependencies, type: identifier_1.ReferenceType.Function }],
out: condition.out,
entryPoint: name.info.id,
exitPoints: [],
graph: information.graph,
environment: information.environment
};
}
(0, assert_1.guard)(condition !== undefined && body !== undefined, () => `While-Loop ${name.content} has no condition or body, impossible!`);
const originalDependency = data.controlDependencies;
if ((0, info_1.alwaysExits)(condition)) {
logger_1.dataflowLogger.warn(`While-Loop ${rootId} forces exit in condition, skipping rest`);
information.graph.addEdge(name.info.id, condition.entryPoint, edge_1.EdgeType.Reads);
return condition;
}
const remainingInputs = (0, linker_1.linkInputs)([
...(0, environment_1.makeAllMaybe)(body.unknownReferences, information.graph, information.environment, false),
...(0, environment_1.makeAllMaybe)(body.in, information.graph, information.environment, false)
], information.environment, [...condition.in, ...condition.unknownReferences], information.graph, true);
(0, linker_1.linkCircularRedefinitionsWithinALoop)(information.graph, (0, linker_1.produceNameSharedIdMap)((0, linker_1.findNonLocalReads)(information.graph, condition.in)), body.out);
// as the while-loop always evaluates its condition
information.graph.addEdge(name.info.id, condition.entryPoint, edge_1.EdgeType.Reads);
return {
unknownReferences: [],
in: [{ nodeId: name.info.id, name: name.lexeme, controlDependencies: originalDependency, type: identifier_1.ReferenceType.Function }, ...remainingInputs],
out: [...(0, environment_1.makeAllMaybe)(body.out, information.graph, information.environment, true), ...condition.out],
entryPoint: name.info.id,
exitPoints: (0, info_1.filterOutLoopExitPoints)(body.exitPoints),
graph: information.graph,
environment: information.environment
};
}
//# sourceMappingURL=built-in-while-loop.js.map