@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
76 lines • 3.25 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isFunctionHigherOrder = isFunctionHigherOrder;
const vertex_1 = require("../graph/vertex");
const assert_1 = require("../../util/assert");
const edge_1 = require("../graph/edge");
const alias_tracking_1 = require("../eval/resolve/alias-tracking");
const config_1 = require("../../config");
const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const general_1 = require("../eval/values/general");
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 ((0, vertex_1.isFunctionDefinitionVertex)(current)) {
return true;
}
const next = graph.outgoingEdges(current.id) ?? [];
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;
}
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 || !(0, vertex_1.isFunctionCallVertex)(caller)) {
continue;
}
for (const arg of caller.args) {
if (arg === r_function_call_1.EmptyArgument) {
continue;
}
const value = (0, general_1.valueSetGuard)((0, alias_tracking_1.resolveIdToValue)(arg.nodeId, { graph, idMap: graph.idMap, resolve: config_1.VariableResolve.Alias, full: true, ctx }));
if (value?.elements.some(e => e.type === 'function-definition')) {
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 || !(0, vertex_1.isFunctionDefinitionVertex)(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