@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
94 lines • 4.73 kB
JavaScript
"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