UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

93 lines 5.9 kB
"use strict"; 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