UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

85 lines 5.14 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 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