@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
170 lines • 8.26 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveFunctionArgument = resolveFunctionArgument;
exports.processApply = processApply;
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 logger_1 = require("../../../../../logger");
const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
const vertex_1 = require("../../../../../graph/vertex");
const edge_1 = require("../../../../../graph/edge");
const unknown_side_effect_1 = require("../../../../../graph/unknown-side-effect");
const identifier_1 = require("../../../../../environments/identifier");
const unnamed_call_handling_1 = require("../unnamed-call-handling");
const linker_1 = require("../../../../linker");
const node_value_1 = require("../../../../../eval/resolve/node-value");
const r_string_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-string");
const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name");
/** Resolve the function an argument stands for: a string literal, a symbol, or an inline definition; `undefined` if none. */
function resolveFunctionArgument(val, data, opts) {
if (opts.unquoteFunction && r_string_1.RString.is(val)) {
return { functionId: val.info.id, functionName: val.content.str, anonymous: false, asString: true };
}
if (val.type === type_1.RType.FunctionDefinition) {
return { functionId: val.info.id, functionName: `${unnamed_call_handling_1.UnnamedFunctionCallPrefix}${val.info.id}`, anonymous: true, asString: false };
}
if (val.type !== type_1.RType.Symbol) {
return undefined;
}
const functionName = opts.resolveValue
? node_value_1.NodeValue.singleStringOf(val.info.id, data)
: val.content;
return functionName === undefined ? undefined : { functionId: val.info.id, functionName, anonymous: false, asString: false };
}
/**
* Process an apply call like `vapply` or `mapply`.
*/
function processApply(name, args, rootId, data, config) {
const { indexOfFunction = 1, nameOfFunctionArgument, unquoteFunction, resolveInEnvironment, resolveValue, hasUnknownSideEffects } = config;
/* as the length is one-based and the argument filter mapping is zero-based, we do not have to subtract 1 */
const forceArgsMask = new Array(indexOfFunction).fill(false);
forceArgsMask.push(true);
const resFn = (0, known_call_handling_1.processKnownFunctionCall)({
name, args, rootId, data, forceArgs: forceArgsMask, origin: built_in_proc_name_1.BuiltInProcName.Apply
});
let information = resFn.information;
if (hasUnknownSideEffects) {
(0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId);
}
const processedArguments = resFn.processedArguments;
let index = indexOfFunction;
/* search, if one of the arguments actually contains the argument name if given in the config */
if (nameOfFunctionArgument !== undefined) {
const mayFn = args.findIndex(arg => arg !== r_function_call_1.EmptyArgument && arg.name?.content === nameOfFunctionArgument);
if (mayFn >= 0) {
index = mayFn;
}
}
// shift the index to point to the index'd unnamed argument
let posArgsFound = 0;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg !== r_function_call_1.EmptyArgument && arg.name) {
// do nothing
}
else if (posArgsFound === index) {
index = i;
break;
}
else {
posArgsFound++;
}
}
/* validate, that we indeed have so many arguments to fill this one :D */
if (index >= args.length) {
logger_1.dataflowLogger.warn(`Function argument at index ${index} not found, skipping`);
return information;
}
const arg = args[index];
if (arg === r_function_call_1.EmptyArgument || !arg.value || (!unquoteFunction && arg.value.type !== type_1.RType.Symbol && arg.value.type !== type_1.RType.FunctionDefinition)) {
logger_1.dataflowLogger.warn(`Expected symbol as argument at index ${index}, but got ${JSON.stringify(arg)} instead.`);
(0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId);
return information;
}
const val = arg.value;
const resolvedFn = resolveFunctionArgument(val, data, { unquoteFunction, resolveValue });
if (resolvedFn === undefined) {
logger_1.dataflowLogger.warn(`Expected symbol or string as function argument at index ${index}, but got ${JSON.stringify(val)} instead.`);
// the called function is dynamic and unresolvable: reached-but-unknown rather than dropped
(0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId);
return information;
}
const { functionName, anonymous, asString } = resolvedFn;
let functionId = resolvedFn.functionId;
if (asString) {
information.in = [...information.in, { type: identifier_1.ReferenceType.Function, name: functionName, cds: data.cds, nodeId: functionId }];
}
const allOtherArguments = processedArguments.map((arg, i) => {
const counterpart = args[i];
if (arg && counterpart !== r_function_call_1.EmptyArgument) {
return {
name: counterpart.name?.content,
valueId: counterpart.value?.info.id,
cds: data.cds,
type: identifier_1.ReferenceType.Argument,
nodeId: arg.entryPoint
};
}
else {
return r_function_call_1.EmptyArgument;
}
}).filter((_, i) => i !== index);
if (anonymous) {
const rootFnId = functionId;
functionId = 'anon-' + rootFnId;
information.graph.addVertex({
tag: vertex_1.VertexType.FunctionCall,
id: functionId,
environment: data.environment,
name: functionName,
/* can never be a direct built-in-call */
onlyBuiltin: false,
cds: data.cds,
args: allOtherArguments, // same reference
origin: [built_in_proc_name_1.BuiltInProcName.Function]
}, data.ctx.env.makeCleanEnv());
information.graph.addEdge(rootId, rootFnId, edge_1.EdgeType.Calls | edge_1.EdgeType.Reads);
information.graph.addEdge(rootId, functionId, edge_1.EdgeType.Calls | edge_1.EdgeType.Argument);
information = {
...information,
in: [
...information.in,
{ type: identifier_1.ReferenceType.Function, name: functionName, cds: data.cds, nodeId: functionId }
]
};
const dfVert = information.graph.getVertex(rootId);
if (dfVert && vertex_1.FunctionDefinitionVertex.is(dfVert)) {
linker_1.ClosureRefs.resolveOpenIngoing(information.graph, rootId, dfVert, data.environment);
}
}
else {
/* identify it as a full-blown function call :) */
information.graph.updateToFunctionCall({
tag: vertex_1.VertexType.FunctionCall,
id: functionId,
name: functionName,
args: allOtherArguments,
environment: resolveInEnvironment === 'global' ? undefined : data.environment,
onlyBuiltin: resolveInEnvironment === 'global',
cds: data.cds,
origin: [built_in_proc_name_1.BuiltInProcName.Function]
});
}
for (const arg of processedArguments) {
if (arg) {
information.graph.addEdge(functionId, arg.entryPoint, edge_1.EdgeType.Argument);
}
}
if (resolveInEnvironment === 'global') {
// remove from open ingoing references
return {
...information,
in: information.in.filter(ref => ref.nodeId !== functionId),
unknownReferences: information.unknownReferences.filter(ref => ref.nodeId !== functionId)
};
}
else {
return information;
}
}
//# sourceMappingURL=built-in-apply.js.map