@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
94 lines • 5.51 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processForLoop = processForLoop;
const processor_1 = require("../../../../../processor");
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 common_1 = require("../common");
const unpack_argument_1 = require("../argument/unpack-argument");
const logger_1 = require("../../../../../logger");
const overwrite_1 = require("../../../../../environments/overwrite");
const define_1 = require("../../../../../environments/define");
const append_1 = require("../../../../../environments/append");
const edge_1 = require("../../../../../graph/edge");
const identifier_1 = require("../../../../../environments/identifier");
const reference_to_maybe_1 = require("../../../../../environments/reference-to-maybe");
const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name");
/**
* Processes a for-loop call: `for(<variable> in <vector>) <body>`
* desugared as:
* ```r
* `for`(<variable>, <vector>, <body>)
* ```
*/
function processForLoop(name, args, rootId, data) {
if (args.length !== 3) {
logger_1.dataflowLogger.warn(`For-Loop ${identifier_1.Identifier.toString(name.content)} does not have three arguments, skipping`);
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information;
}
const [variableArg, vectorArg, bodyArg] = args.map(e => (0, unpack_argument_1.unpackNonameArg)(e));
// we store the original environment here, as we merge it back lter in case the for-loop never executes
const origEnv = data.environment;
(0, assert_1.guard)(variableArg !== undefined && vectorArg !== undefined && bodyArg !== undefined, () => `For-Loop ${JSON.stringify(args)} has missing arguments! Bad!`);
const vector = (0, processor_1.processDataflowFor)(vectorArg, data);
if ((0, info_1.alwaysExits)(vector)) {
logger_1.dataflowLogger.warn(`For-Loop ${rootId} forces exit in vector, skipping rest`);
return vector;
}
const variable = (0, processor_1.processDataflowFor)(variableArg, data);
// this should not be able to exit always!
const originalDependency = data.cds;
let headEnvironments = (0, overwrite_1.overwriteEnvironment)(vector.environment, variable.environment);
const headGraph = variable.graph.mergeWith(vector.graph);
const writtenVariable = variable.unknownReferences.concat(variable.in);
const writtenIds = new Set();
for (const write of writtenVariable) {
writtenIds.add(write.nodeId);
headEnvironments = (0, define_1.define)({ ...write, definedAt: name.info.id, type: identifier_1.ReferenceType.Variable }, false, headEnvironments);
}
data.environment = headEnvironments;
const body = (0, processor_1.processDataflowFor)(bodyArg, data);
const outEnvironment = (0, append_1.appendEnvironment)(headEnvironments, body.environment);
const cd = [{ id: name.info.id, when: true }];
const bodyRefs = body.in.concat(body.unknownReferences);
(0, reference_to_maybe_1.applyCdsToAllInGraphButConstants)(body.graph, bodyRefs, cd);
const nextGraph = headGraph.mergeWith(body.graph);
// now we have to identify all reads that may be effected by a circular redefinition
// for this, we search for all reads with a non-local read resolve!
const nameIdShares = (0, linker_1.produceNameSharedIdMap)((0, linker_1.findNonLocalReads)(nextGraph, writtenIds));
for (const write of writtenVariable) {
nextGraph.addEdge(write.nodeId, vector.entryPoint, edge_1.EdgeType.DefinedBy);
nextGraph.setDefinitionOfVertex(write, [vector.entryPoint]);
}
(0, reference_to_maybe_1.applyCdToReferences)(body.out, cd);
const outgoing = variable.out.concat(writtenVariable, body.out);
(0, linker_1.linkCircularRedefinitionsWithinALoop)(nextGraph, nameIdShares, body.out);
(0, linker_1.reapplyLoopExitPoints)(body.exitPoints, body.in.concat(body.out, body.unknownReferences), nextGraph);
(0, common_1.patchFunctionCall)({
nextGraph,
rootId,
name,
data: { ...data, cds: originalDependency },
argumentProcessResult: [variable, vector, body],
origin: built_in_proc_name_1.BuiltInProcName.ForLoop
});
/* mark the last argument as nse */
nextGraph.addEdge(rootId, body.entryPoint, edge_1.EdgeType.NonStandardEvaluation);
// as the for-loop always evaluates its condition
nextGraph.addEdge(name.info.id, vector.entryPoint, edge_1.EdgeType.Reads);
return {
unknownReferences: [],
// we only want those not bound by a local variable
in: [{ nodeId: rootId, name: name.content, cds: originalDependency, type: identifier_1.ReferenceType.Function }, ...vector.unknownReferences, ...[...nameIdShares.values()].flat()],
out: outgoing,
graph: nextGraph,
entryPoint: name.info.id,
exitPoints: (0, info_1.filterOutLoopExitPoints)(body.exitPoints),
// if we can not be sure that the for-loop runs once, we have to merge back the original environment, as the body may never execute
environment: (0, append_1.appendEnvironment)(origEnv, outEnvironment),
hooks: variable.hooks.concat(vector.hooks, body.hooks),
};
}
//# sourceMappingURL=built-in-for-loop.js.map