UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

95 lines 4.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isFunctionHigherOrder = isFunctionHigherOrder; const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id"); const identifier_1 = require("../environments/identifier"); const vertex_1 = require("../graph/vertex"); const assert_1 = require("../../util/assert"); const edge_1 = require("../graph/edge"); const node_value_1 = require("../eval/resolve/node-value"); const config_1 = require("../../config"); const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call"); const resolve_helper_1 = require("../environments/resolve-helper"); const graph_1 = require("../graph/graph"); function isAnyReturnAFunction(def, graph) { const workingQueue = def.exitPoints.map(d => graph.getVertex(d.nodeId)).filter(assert_1.isNotUndefined); const seen = new Set(); while (workingQueue.length > 0) { const current = workingQueue.pop(); if (seen.has(current.id)) { continue; } seen.add(current.id); if (vertex_1.FunctionDefinitionVertex.is(current)) { return true; } const next = graph.outgoingEdges(current.id) ?? graph_1.NoEdges; for (const [t, e] of next) { if (edge_1.DfEdge.includesType(e, edge_1.EdgeType.Returns)) { const v = graph.getVertex(t); if (v) { workingQueue.push(v); } } } } return false; } /** * Whether the argument hands over a built-in function (`f(print)`), which has no definition in the graph and * hence no `function-definition` value to resolve to. */ function readsBuiltInFunction(id, graph, ctx) { for (const [target, edge] of graph.outgoingEdges(id) ?? graph_1.NoEdges) { if (!edge_1.DfEdge.includesType(edge, edge_1.EdgeType.Reads) || !node_id_1.NodeId.isBuiltIn(target)) { continue; } const defs = resolve_helper_1.Resolve.byNameAndType(node_id_1.NodeId.fromBuiltIn(target), ctx.env.makeCleanEnv(), identifier_1.ReferenceType.Function); if (defs?.some(d => (0, identifier_1.isReferenceType)(d.type, identifier_1.ReferenceType.BuiltInFunction))) { return true; } } return false; } function inspectCallSitesArgumentsFns(def, graph, ctx, invertedGraph) { const callSites = invertedGraph?.outgoingEdges(def.id) ?? graph.ingoingEdges(def.id); for (const [callerId, e] of callSites ?? []) { if (!edge_1.DfEdge.includesType(e, edge_1.EdgeType.Calls)) { continue; } const caller = graph.getVertex(callerId); if (!caller || !vertex_1.FunctionCallVertex.is(caller)) { continue; } for (const arg of caller.args) { if (arg === r_function_call_1.EmptyArgument) { continue; } const value = node_value_1.NodeValue.inGraph.setOf(arg.nodeId, graph, ctx, { resolve: config_1.VariableResolve.Alias }); if (value?.elements.some(e => e.type === 'function-definition') || readsBuiltInFunction(arg.nodeId, graph, ctx)) { return true; } } } return false; } /** * Determines whether the function with the given id is a higher-order function, i.e., * either takes a function as an argument or (may) returns a function. * If the return is an identity, e.g., `function(x) x`, this is not considered higher-order, * if no function is passed as an argument. * Please note that inspecting higher order functions can be sped up (if queries multiple times) by providing an inverted graph as well! */ function isFunctionHigherOrder(id, graph, ctx, invertedGraph) { const vert = graph.getVertex(id); if (!vert || !vertex_1.FunctionDefinitionVertex.is(vert)) { return false; } // 1. check whether any of the exit types is a function if (isAnyReturnAFunction(vert, graph)) { return true; } // 2. check whether any of the callsites passes a function return inspectCallSitesArgumentsFns(vert, graph, ctx, invertedGraph); } //# sourceMappingURL=higher-order-function.js.map