UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

179 lines 9.02 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processPurrrFormula = processPurrrFormula; const known_call_handling_1 = require("../known-call-handling"); const linker_1 = require("../../../../linker"); const df_helper_1 = require("../../../../../graph/df-helper"); const vertex_1 = require("../../../../../graph/vertex"); const resolve_by_name_1 = require("../../../../../environments/resolve-by-name"); const identifier_1 = require("../../../../../environments/identifier"); const common_1 = require("../common"); const r_argument_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-argument"); const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name"); const model_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/model"); const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type"); const edge_1 = require("../../../../../graph/edge"); const unpack_argument_1 = require("../argument/unpack-argument"); const logger_1 = require("../../../../../logger"); const graph_1 = require("../../../../../graph/graph"); function linkOnSymbol(rootId, filteredArgs, node, graph, data) { // If the formula is a symbol naming a function, try to resolve it in the call environment. try { const defs = (0, resolve_by_name_1.resolveByName)(node.content, data.environment, identifier_1.ReferenceType.Function) ?? []; graph.addEdge(rootId, node.info.id, edge_1.EdgeType.Calls); for (const def of defs) { // Mark the call as calling this target graph.addEdge(rootId, def.nodeId, edge_1.EdgeType.Calls); // If the target directly maps to a function definition AST, try to link arguments to parameters let linked = data.completeAst.idMap.get(def.nodeId); if (linked?.type !== type_1.RType.FunctionDefinition) { for (const vid of def.value) { const candidate = data.completeAst.idMap.get(vid); if (candidate && candidate.type === type_1.RType.FunctionDefinition) { linked = candidate; // also mark that we call the resolved function-definition node graph.addEdge(rootId, vid, edge_1.EdgeType.Calls); break; } } } // we may find a candidate in the first check if (linked?.type === type_1.RType.FunctionDefinition) { try { return (0, linker_1.linkArgumentsOnCall)(filteredArgs, linked.parameters, graph); } catch (e) { logger_1.dataflowLogger.warn('Failed to link arguments to parameters for purr formula (symbol target), some bindings may be missing', { error: e }); } } } } catch (e) { logger_1.dataflowLogger.warn('Failed to resolve symbol for purrr formula .f', { error: e }); } } /** * Support for R's purr formula: `map(df, ~ .x + 1)`. */ function processPurrrFormula(name, args, rootId, data, config) { const { information, callArgs } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: built_in_proc_name_1.BuiltInProcName.PurrrFormula }); const params = {}; for (const key of Object.keys(config.args)) { params[config.args[key].name] = config.args[key].name; } // formula parameter params[config['.f'].name] = config['.f'].name; params['...'] = '...'; const argMaps = (0, linker_1.pMatch)((0, common_1.convertFnArguments)(args), params); const formulaArgId = argMaps.get(config['.f'].name)?.[0]; if (!formulaArgId) { // nothing to do if we couldn't find the formula argument return information; } const formulaArg = r_argument_1.RArgument.getWithId(args, formulaArgId); const formulaNode = formulaArg ? (0, unpack_argument_1.unpackNonameArg)(formulaArg) : undefined; if (!formulaNode) { return information; } let argToParamMap = new Map(); // Prepare the list of call arguments to consider for linking (exclude the formula argument and ignored args) const filteredCallArgs = []; for (const arg of callArgs) { const aid = graph_1.FunctionArgument.getId(arg); if (aid === formulaArgId || aid === formulaNode.info.id || config.ignore?.includes(graph_1.FunctionArgument.getName(arg) ?? '')) { continue; } filteredCallArgs.push(arg); } if (formulaNode.type === type_1.RType.FunctionDefinition) { const fdef = formulaNode; information.graph.addEdge(rootId, fdef.info.id, edge_1.EdgeType.Calls); try { argToParamMap = (0, linker_1.linkArgumentsOnCall)(filteredCallArgs, fdef.parameters, information.graph); } catch (e) { logger_1.dataflowLogger.warn('Failed to link arguments to parameters for purr formula, some bindings may be missing', { error: e }); } } else if (formulaNode.type === type_1.RType.Symbol) { linkOnSymbol(rootId, filteredCallArgs, formulaNode, information.graph, data); } else { try { df_helper_1.Dataflow.visitDfg(information.graph, formulaNode.info.id, (vtx) => { if (vtx.tag === vertex_1.VertexType.FunctionCall) { information.graph.addEdge(rootId, vtx.id, edge_1.EdgeType.Calls); const targets = (0, linker_1.getAllFunctionCallTargets)(vtx.id, information.graph, vtx.environment); for (const t of targets) { information.graph.addEdge(rootId, t, edge_1.EdgeType.Calls); const linked = data.completeAst.idMap.get(t); if (linked && linked.type === type_1.RType.FunctionDefinition) { try { const map = (0, linker_1.linkArgumentsOnCall)(filteredCallArgs, linked.parameters, information.graph); for (const [argId, paramId] of map.entries()) { if (!argToParamMap.has(argId)) { argToParamMap.set(argId, paramId); } } } catch (e) { logger_1.dataflowLogger.warn('Failed to link arguments to parameters for purrr formula (list target)', { error: e }); } } } return !vtx.origin.includes(built_in_proc_name_1.BuiltInProcName.List); } else if (vtx.tag === vertex_1.VertexType.Use) { const node = data.completeAst.idMap.get(vtx.id); if (node?.type === type_1.RType.Symbol) { linkOnSymbol(rootId, filteredCallArgs, node, information.graph, data); } } return false; }); } catch (e) { logger_1.dataflowLogger.warn('Failed to traverse sub-dfg for purrr formula .f', { error: e }); } } const ignore = new Set(config.ignore ?? []); model_1.RNode.visitAst(formulaNode, (node) => { if (node.type === type_1.RType.Symbol) { const sym = node; const name = sym.content; if (ignore.has(name)) { return false; } const mappingKey = config.args[name]?.name; if (mappingKey) { const pid = (argMaps.get(mappingKey) ?? [])[0]; if (!pid) { return false; } const arg = r_argument_1.RArgument.getWithId(args, pid); const producedNode = arg ? (0, unpack_argument_1.unpackNonameArg)(arg) : undefined; if (!producedNode) { return false; } // If this argument was linked to a parameter on the call, skip adding edge const resultId = producedNode.info.id; if (!argToParamMap.has(resultId)) { information.graph.addEdge(node.info.id, resultId, edge_1.EdgeType.Reads); } } } return false; }); if (config.returnArg) { const returnArgId = argMaps.get(config.returnArg)?.[0]; if (returnArgId) { const returnArg = r_argument_1.RArgument.getWithId(args, returnArgId); const producedNode = returnArg ? (0, unpack_argument_1.unpackNonameArg)(returnArg) : undefined; if (producedNode) { information.graph.addEdge(rootId, producedNode.info.id, edge_1.EdgeType.Returns); } } } return information; } //# sourceMappingURL=built-in-purrr-formula.js.map