UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

157 lines 7.51 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NseKind = exports.NseArguments = void 0; exports.markArgumentsAsNonStandardEvaluation = markArgumentsAsNonStandardEvaluation; exports.processKnownFunctionCall = processKnownFunctionCall; const processor_1 = require("../../../../processor"); const common_1 = require("./common"); const identifier_1 = require("../../../../environments/identifier"); const graph_1 = require("../../../../graph/graph"); const edge_1 = require("../../../../graph/edge"); const logger_1 = require("../../../../logger"); const vertex_1 = require("../../../../graph/vertex"); const unknown_side_effect_1 = require("../../../../graph/unknown-side-effect"); const built_in_proc_name_1 = require("../../../../environments/built-in-proc-name"); const nse_1 = require("./nse"); /** * Which arguments of a call {@link markArgumentsAsNonStandardEvaluation|are non-standardly evaluated}: * every argument, all but the (data) first one, or only the first one. */ var NseArguments; (function (NseArguments) { /** every argument, e.g. the operands of a formula `~` or `aes(x, y)` */ NseArguments["All"] = "all"; /** all but the first, e.g. `subset(data, col > 1)` where the first argument is the data object */ NseArguments["AllButFirst"] = "all-but-first"; /** only the first, e.g. the native routine name in `.C(routine, ...)` */ NseArguments["First"] = "first"; })(NseArguments || (exports.NseArguments = NseArguments = {})); /** * How an argument escapes standard evaluation, which decides what is marked * {@link EdgeType.NonStandardEvaluation}. */ var NseKind; (function (NseKind) { /** the argument is not evaluated at all (`quote(x + y)`), so nothing within it is */ NseKind["Quoted"] = "quoted"; /** the argument is evaluated in a data mask (`subset(d, a > k)`), where only its symbols may name columns */ NseKind["DataMasked"] = "data-masked"; /** evaluated, just not exactly once (a loop body), so only the argument itself is marked */ NseKind["Reevaluated"] = "reevaluated"; })(NseKind || (exports.NseKind = NseKind = {})); /** * Marks the selected arguments as {@link EdgeType.NonStandardEvaluation}: a {@link NseKind.Quoted|quoted} one * entirely, a {@link NseKind.DataMasked|data-masked} one only where {@link Nse.suppliedByMask} holds, and a * {@link NseKind.Reevaluated|reevaluated} one as a whole but not within. */ function markArgumentsAsNonStandardEvaluation(graph, rootId, processedArguments, which, { kind = NseKind.Quoted, evaluated } = {}) { if (which === undefined) { return; } if (typeof which !== 'string') { for (const i of which) { if (i < processedArguments.length) { markArgument(graph, rootId, processedArguments[i], kind, evaluated); } else { logger_1.dataflowLogger.warn(`Trying to mark argument ${i} as non-standard-evaluation, but only ${processedArguments.length} arguments are available`); } } return; } const end = which === NseArguments.First ? 1 : processedArguments.length; for (let i = which === NseArguments.AllButFirst ? 1 : 0; i < end; i++) { markArgument(graph, rootId, processedArguments[i], kind, evaluated); } } function markArgument(graph, rootId, arg, kind, evaluated) { if (arg === undefined) { return; } if (kind !== NseKind.DataMasked) { graph.addEdge(rootId, arg.entryPoint, edge_1.EdgeType.NonStandardEvaluation); } if (kind === NseKind.Reevaluated) { return; } for (const [vtx, info] of arg.graph.vertices(true)) { if (evaluated?.has(vtx)) { continue; } /* every name in a mask is a candidate column, even one the caller binds: R asks the data first. {@link Nse.dropResolvedMask} takes the mark off the bound ones again, and keeps them as the names that mean both */ if (kind === NseKind.Quoted || nse_1.Nse.maskCandidate(info)) { graph.addEdge(rootId, vtx, edge_1.EdgeType.NonStandardEvaluation); } } } /** * The main processor for function calls for which we know the target but need not * add any specific handling. */ function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = false, markAsNSE = undefined, forceArgs, patchData = d => d, hasUnknownSideEffect, origin, nonFunction }) { const functionName = (0, processor_1.processDataflowFor)(name, data); const finalGraph = new graph_1.DataflowGraph(data.completeAst.idMap); const functionCallName = name.content; const processArgs = reverseOrder ? args.toReversed() : args; const { finalEnv, callArgs, remainingReadInArgs, processedArguments } = (0, common_1.processAllArguments)({ functionName, args: processArgs, data, finalGraph, functionRootId: rootId, patchData, forceArgs, nonFunction }); markArgumentsAsNonStandardEvaluation(finalGraph, rootId, processedArguments, markAsNSE, { kind: NseKind.Reevaluated }); const onlyBuiltin = data.builtInNoEnv === rootId; finalGraph.addVertex({ tag: vertex_1.VertexType.FunctionCall, id: rootId, environment: onlyBuiltin ? undefined : data.environment, name: functionCallName, /* may still be overwritten by markAsOnlyBuiltIn */ onlyBuiltin, cds: data.cds, args: reverseOrder ? callArgs.toReversed() : callArgs, origin: origin === 'default' ? [built_in_proc_name_1.BuiltInProcName.Function] : [origin] }, data.ctx.env.makeCleanEnv()); if (hasUnknownSideEffect) { (0, unknown_side_effect_1.handleUnknownSideEffect)(finalGraph, data.environment, rootId); } const inIds = remainingReadInArgs; const fnRef = { nodeId: rootId, name: functionCallName, cds: data.cds, type: identifier_1.ReferenceType.Function }; inIds.push(fnRef); // if force args is not none, we need to collect all non-default exit points from our arguments! let exitPoints = undefined; if (forceArgs === 'all') { const nonDefaults = processedArguments.flatMap(p => p ? p.exitPoints.filter(ep => ep.type !== 0 /* ExitPointType.Default */) : []); if (nonDefaults.length > 0) { exitPoints = nonDefaults; } } else if (forceArgs) { for (let i = 0; i < forceArgs.length; i++) { if (forceArgs[i]) { const p = processedArguments[i]; if (p) { const nonDefaults = p.exitPoints.filter(ep => ep.type !== 0 /* ExitPointType.Default */); if (nonDefaults.length > 0) { exitPoints ??= []; exitPoints.push(...nonDefaults); } } } } } return { information: { unknownReferences: [], in: inIds, /* we do not keep the argument out as it has been linked by the function */ out: functionName.out, graph: finalGraph, environment: finalEnv, entryPoint: rootId, exitPoints: exitPoints ?? [{ nodeId: rootId, type: 0 /* ExitPointType.Default */, cds: data.cds }], hooks: functionName.hooks }, callArgs, processedArguments: reverseOrder ? processedArguments.toReversed() : processedArguments, fnRef }; } //# sourceMappingURL=known-call-handling.js.map