@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
93 lines • 5.9 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 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");
const reference_to_maybe_1 = require("../../../../../environments/reference-to-maybe");
const append_1 = require("../../../../../environments/append");
const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name");
/**
* Process a while loop like `while(cond) { ... }`.
*/
function processWhileLoop(name, args, rootId, data) {
if (args.length !== 2 || args[1] === r_function_call_1.EmptyArgument) {
logger_1.dataflowLogger.warn(`While-Loop ${identifier_1.Identifier.toString(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.unpackNonameArg)(e));
if (unpackedArgs.some(assert_1.isUndefined)) {
logger_1.dataflowLogger.warn(`While-Loop ${identifier_1.Identifier.toString(name.content)} has empty arguments in ${JSON.stringify(args)}, skipping`);
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information;
}
const nameId = name.info.id;
const origEnv = data.environment;
// 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, resolve: data.ctx.config.solver.variables, ctx: data.ctx });
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],
origin: built_in_proc_name_1.BuiltInProcName.WhileLoop
});
const [condition, body] = processedArguments;
// If the condition is always false, we don't include the body
if (condition !== undefined && conditionIsAlwaysFalse) {
information.graph.addEdge(nameId, condition.entryPoint, edge_1.EdgeType.Reads);
return {
unknownReferences: [],
in: [{ nodeId: nameId, name: name.lexeme, cds: data.cds, type: identifier_1.ReferenceType.Function }],
out: condition.out,
entryPoint: nameId,
exitPoints: [],
graph: information.graph,
environment: information.environment,
hooks: condition.hooks
};
}
const conditionIsAlwaysTrue = (0, general_1.valueSetGuard)(values)?.elements.every(d => d.type === 'logical' && d.value === true) ?? false;
(0, assert_1.guard)(condition !== undefined && body !== undefined, () => `While-Loop ${identifier_1.Identifier.toString(name.content)} has no condition or body, impossible!`);
const originalDependency = data.cds;
if ((0, info_1.alwaysExits)(condition)) {
logger_1.dataflowLogger.warn(`While-Loop ${rootId} forces exit in condition, skipping rest`);
information.graph.addEdge(nameId, condition.entryPoint, edge_1.EdgeType.Reads);
return condition;
}
const cdTrue = [{ id: nameId, when: true }];
const bodyRead = body.in.concat(body.unknownReferences);
(0, reference_to_maybe_1.applyCdsToAllInGraphButConstants)(body.graph, bodyRead, cdTrue);
const remainingInputs = (0, linker_1.linkInputs)(bodyRead, information.environment, condition.in.concat(condition.unknownReferences), information.graph, true);
(0, reference_to_maybe_1.applyCdToReferences)(body.out, cdTrue);
(0, linker_1.linkCircularRedefinitionsWithinALoop)(information.graph, (0, linker_1.produceNameSharedIdMap)((0, linker_1.findNonLocalReads)(information.graph, new Set(condition.in.map(i => i.nodeId)))), body.out);
(0, linker_1.reapplyLoopExitPoints)(body.exitPoints, body.in.concat(body.out, body.unknownReferences), information.graph);
// as the while-loop always evaluates its condition
information.graph.addEdge(nameId, condition.entryPoint, edge_1.EdgeType.Reads);
return {
unknownReferences: [],
in: [{ nodeId: nameId, name: name.lexeme, cds: originalDependency, type: identifier_1.ReferenceType.Function }, ...remainingInputs],
out: condition.out.concat(body.out),
entryPoint: nameId,
exitPoints: (0, info_1.filterOutLoopExitPoints)(body.exitPoints),
graph: information.graph,
// as we do not know whether the loop executes at all, we have to merge the environments of the condition and the body, as both may be relevant
environment: conditionIsAlwaysTrue ? information.environment : (0, append_1.appendEnvironment)(origEnv, information.environment),
hooks: condition.hooks.concat(body.hooks)
};
}
//# sourceMappingURL=built-in-while-loop.js.map