@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
90 lines • 5.11 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processWithEnv = processWithEnv;
const processor_1 = require("../../../../../processor");
const known_call_handling_1 = require("../known-call-handling");
const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const identifier_1 = require("../../../../../environments/identifier");
const built_in_envir_utils_1 = require("./built-in-envir-utils");
const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name");
const common_1 = require("../common");
const edge_1 = require("../../../../../graph/edge");
const linker_1 = require("../../../../linker");
/** Formal parameter names for `with(data, expr, ...)` and `within(data, expr, ...)`. */
const withParams = ['data', 'expr'];
/**
* Processes `with(data, expr)` and `within(data, expr)`.
*
* When `data` is a variable that holds a tracked {@link InGraphIdentifierDefinition#envState},
* the expression `expr` is evaluated in that environment's scope so that reads of names
* defined there resolve correctly.
*
* - `with`: writes in `expr` are ephemeral (R's `with` uses a temporary scope and discards them).
* - `within`: writes in `expr` are persisted back into the tracked envState of `data`.
*
* Arguments are resolved using R's standard matching rules ({@link bindArgs}: pmatch for named
* args, positional fallback), so `with(expr=x, data=e)` and `with(dat=e, x)` are handled correctly.
*
* Falls back to a normal function-call analysis when the data argument cannot be resolved
* to a tracked environment.
*/
function processWithEnv(name, args, rootId, data) {
if (args.length < 2) {
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: built_in_proc_name_1.BuiltInProcName.With }).information;
}
const bound = (0, built_in_envir_utils_1.bindArgs)(args, withParams);
const dataArg = bound.get('data');
const exprArg = bound.get('expr');
if (dataArg === undefined || dataArg === r_function_call_1.EmptyArgument || dataArg.value === undefined ||
exprArg === undefined || exprArg === r_function_call_1.EmptyArgument || exprArg.value === undefined) {
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: built_in_proc_name_1.BuiltInProcName.With }).information;
}
const envirResolution = (0, built_in_envir_utils_1.resolveArgToEnvir)(dataArg, data);
if (!envirResolution) {
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: built_in_proc_name_1.BuiltInProcName.With }).information;
}
/* evaluate data arg in the caller's scope (it is just read) */
const dfDataArg = (0, processor_1.processDataflowFor)(dataArg.value, data);
/* evaluate expr in the resolved env's scope so variable lookup uses envState */
const dfExpr = (0, processor_1.processDataflowFor)(exprArg.value, {
...data,
environment: envirResolution.envirData.environment
});
/*
* processSymbol puts symbol uses in unknownReferences without creating reads edges.
* Explicitly link expr's unknown references against the envState so reads edges are
* created for names that resolve there (e.g. `x` in `with(e, x)` -> assign).
* References that cannot be resolved in envState are kept for the outer scope.
*/
const remainingFromExpr = [];
(0, linker_1.linkInputs)(dfExpr.unknownReferences, envirResolution.envirData.environment, remainingFromExpr, dfExpr.graph, false);
(0, common_1.patchFunctionCall)({
nextGraph: dfDataArg.graph,
rootId,
name,
data,
argumentProcessResult: [dfDataArg, dfExpr],
origin: built_in_proc_name_1.BuiltInProcName.With
});
const merged = dfDataArg.graph.mergeWith(dfExpr.graph);
merged.addEdge(rootId, envirResolution.envirNodeId, edge_1.EdgeType.Reads);
const ingoing = dfDataArg.in.concat(dfExpr.in, dfDataArg.unknownReferences, remainingFromExpr, /* unresolved expr refs propagate to outer scope */ [{ nodeId: rootId, name: name.content, cds: data.cds, type: identifier_1.ReferenceType.Function }]);
/* within routes body writes back into the data environment; with discards them */
const isWithin = identifier_1.Identifier.getName(name.content) === 'within';
let resultEnv = data.environment;
if (isWithin && dfExpr.out.length > 0) {
const tempResult = { ...dfExpr, environment: data.environment };
resultEnv = (0, built_in_envir_utils_1.routeWrittenToCustomEnv)(tempResult, envirResolution.envDef, rootId).environment;
}
return {
hooks: dfDataArg.hooks.concat(dfExpr.hooks),
environment: resultEnv,
exitPoints: dfDataArg.exitPoints.concat(dfExpr.exitPoints),
graph: merged,
entryPoint: rootId,
in: ingoing,
out: [], /* writes are inside envState, not directly in outer scope */
unknownReferences: []
};
}
//# sourceMappingURL=built-in-with.js.map