UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

90 lines 5.11 kB
"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