@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
157 lines • 7.51 kB
JavaScript
"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