UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

172 lines 8.41 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 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 identifier_1 = require("../../../../../environments/identifier"); const resolve_by_name_1 = require("../../../../../environments/resolve-by-name"); const unnamed_call_handling_1 = require("../unnamed-call-handling"); const general_1 = require("../../../../../eval/values/general"); const r_value_1 = require("../../../../../eval/values/r-value"); const log_1 = require("../../../../../../util/log"); const alias_tracking_1 = require("../../../../../eval/resolve/alias-tracking"); function processApply(name, args, rootId, data, config) { const { indexOfFunction = 1, nameOfFunctionArgument, unquoteFunction, resolveInEnvironment, resolveValue } = 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: 'builtin:apply' }); let information = resFn.information; 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 && arg.name.content === nameOfFunctionArgument); if (mayFn >= 0) { index = mayFn; } } /* 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.`); return information; } let functionId = undefined; let functionName = undefined; let anonymous = false; const val = arg.value; if (unquoteFunction && val.type === type_1.RType.String) { functionId = val.info.id; functionName = val.content.str; information = { ...information, in: [...information.in, { type: identifier_1.ReferenceType.Function, name: functionName, controlDependencies: data.controlDependencies, nodeId: functionId }] }; } else if (val.type === type_1.RType.Symbol) { functionId = val.info.id; if (resolveValue) { const resolved = (0, general_1.valueSetGuard)((0, alias_tracking_1.resolveIdToValue)(val.info.id, { environment: data.environment, idMap: data.completeAst.idMap })); if (resolved?.elements.length === 1 && resolved.elements[0].type === 'string') { functionName = (0, r_value_1.isValue)(resolved.elements[0].value) ? resolved.elements[0].value.str : undefined; } } else { functionName = val.content; } } else if (val.type === type_1.RType.FunctionDefinition) { anonymous = true; functionId = val.info.id; functionName = `${unnamed_call_handling_1.UnnamedFunctionCallPrefix}${functionId}`; } if (functionName === undefined || functionId === undefined) { logger_1.dataflowLogger.warn(`Expected symbol or string as function argument at index ${index}, but got ${JSON.stringify(val)} instead.`); return information; } const allOtherArguments = processedArguments.map((arg, i) => { const counterpart = args[i]; if (arg && counterpart !== r_function_call_1.EmptyArgument) { return { name: counterpart.name?.content, controlDependencies: data.controlDependencies, 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.controlDependencies, args: allOtherArguments, // same reference origin: ['function'] }); 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, controlDependencies: data.controlDependencies, nodeId: functionId } ] }; const dfVert = information.graph.getVertex(rootId); if (dfVert && dfVert.tag === vertex_1.VertexType.FunctionDefinition) { // resolve all ingoings against the environment const ingoingRefs = dfVert.subflow.in; const remainingIn = []; for (const ingoing of ingoingRefs) { const resolved = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, data.environment, ingoing.type) : undefined; if (resolved === undefined) { remainingIn.push(ingoing); continue; } (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${ingoing.nodeId} in closure of function definition ${rootId}`); let allBuiltIn = true; for (const ref of resolved) { information.graph.addEdge(ingoing, ref, edge_1.EdgeType.Reads); information.graph.addEdge(rootId, ref, edge_1.EdgeType.Reads); // because the def. is the anonymous call if (!(0, identifier_1.isReferenceType)(ref.type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) { allBuiltIn = false; } } if (allBuiltIn) { remainingIn.push(ingoing); } } dfVert.subflow.in = remainingIn; } } 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.controlDependencies, origin: ['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