UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

94 lines 4.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.UnnamedFunctionCallPrefix = void 0; exports.processUnnamedFunctionCall = processUnnamedFunctionCall; const processor_1 = require("../../../../processor"); const common_1 = require("./common"); const linker_1 = require("../../../linker"); const edge_1 = require("../../../../graph/edge"); const graph_1 = require("../../../../graph/graph"); const unknown_side_effect_1 = require("../../../../graph/unknown-side-effect"); const vertex_1 = require("../../../../graph/vertex"); const type_1 = require("../../../../../r-bridge/lang-4.x/ast/model/type"); const logger_1 = require("../../../../logger"); const identifier_1 = require("../../../../environments/identifier"); const built_in_proc_name_1 = require("../../../../environments/built-in-proc-name"); const node_id_1 = require("../../../../../r-bridge/lang-4.x/ast/model/processing/node-id"); exports.UnnamedFunctionCallPrefix = 'unnamed-fc-'; /** * Whether a `$`/`[[` access callee resolved a field to a stored function: a resolved dispatch (`dispatch$foo()`) * carries a `Returns` edge from the access node to the field target (in addition to the base `Returns` to `accessedId`), * while an opaque object (`g$greet()` on an untracked `g`) only carries the base `Returns`. */ function accessResolvesToField(graph, accessId, accessedId) { const outgoing = graph.outgoingEdges(accessId); if (outgoing === undefined) { return false; } const base = node_id_1.NodeId.normalize(accessedId); for (const [target, edge] of outgoing) { if (node_id_1.NodeId.normalize(target) !== base && edge_1.DfEdge.includesType(edge, edge_1.EdgeType.Returns)) { return true; } } return false; } /** * Processes an unnamed function call. * For example `(function(x) { x + 1 })(5)` */ function processUnnamedFunctionCall(functionCall, data) { const calledFunction = (0, processor_1.processDataflowFor)(functionCall.calledFunction, data); const finalGraph = new graph_1.DataflowGraph(data.completeAst.idMap); const functionRootId = functionCall.info.id; const calledRootId = functionCall.calledFunction.info.id; const functionCallName = `${exports.UnnamedFunctionCallPrefix}${functionRootId}`; logger_1.dataflowLogger.debug(`Using ${functionRootId} as root for the unnamed function call`); // we know that it reads the toplevel: finalGraph.addEdge(functionRootId, calledRootId, edge_1.EdgeType.Reads); // keep the defined function finalGraph.mergeWith(calledFunction.graph); const { finalEnv, callArgs, remainingReadInArgs } = (0, common_1.processAllArguments)({ functionName: calledFunction, args: functionCall.arguments, data, finalGraph, functionRootId /* we know the call is right there and fully resolved, there is no need to artificially force arguments as we identify them within the subtree */ }); finalGraph.addVertex({ tag: vertex_1.VertexType.FunctionCall, id: functionRootId, environment: data.environment, name: functionCallName, /* can never be a direct built-in-call */ onlyBuiltin: false, cds: data.cds, args: callArgs, // same reference origin: [built_in_proc_name_1.BuiltInProcName.Unnamed] }, data.ctx.env.makeCleanEnv()); let inIds = remainingReadInArgs; inIds.push({ nodeId: functionRootId, name: functionCallName, cds: data.cds, type: identifier_1.ReferenceType.Function }); // if we just call a nested fdef if (functionCall.calledFunction.type === type_1.RType.FunctionDefinition) { (0, linker_1.linkArgumentsOnCall)(callArgs, functionCall.calledFunction.parameters, finalGraph); } else if (functionCall.calledFunction.type === type_1.RType.Access && !accessResolvesToField(finalGraph, calledRootId, functionCall.calledFunction.accessed.info.id)) { // `obj$method()` whose callee did not resolve to a stored function: reached-but-unknown rather than dropped (0, unknown_side_effect_1.handleUnknownSideEffect)(finalGraph, data.environment, functionRootId); } // push the called function to the ids: inIds = inIds.concat(calledFunction.in, calledFunction.unknownReferences); return { unknownReferences: [], in: inIds, // we do not keep the argument out as it has been linked by the function out: calledFunction.out, graph: finalGraph, environment: finalEnv, entryPoint: functionCall.info.id, exitPoints: calledFunction.exitPoints, hooks: calledFunction.hooks, }; } //# sourceMappingURL=unnamed-call-handling.js.map