@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
88 lines • 5.19 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAllFunctionCallTargets = getAllFunctionCallTargets;
exports.sliceForCall = sliceForCall;
exports.handleReturns = handleReturns;
const assert_1 = require("../../util/assert");
const fingerprint_1 = require("./fingerprint");
const linker_1 = require("../../dataflow/internal/linker");
const graph_1 = require("../../dataflow/graph/graph");
const built_in_1 = require("../../dataflow/environments/built-in");
const resolve_by_name_1 = require("../../dataflow/environments/resolve-by-name");
const edge_1 = require("../../dataflow/graph/edge");
const identifier_1 = require("../../dataflow/environments/identifier");
const built_in_function_definition_1 = require("../../dataflow/internal/process/functions/call/built-in/built-in-function-definition");
const static_slicer_1 = require("./static-slicer");
/**
* Returns the function call targets (definitions) by the given caller
*/
function getAllFunctionCallTargets(dataflowGraph, callerInfo, baseEnvironment, queue) {
// bind with call-local environments during slicing
const outgoingEdges = dataflowGraph.get(callerInfo.id, true);
(0, assert_1.guard)(outgoingEdges !== undefined, () => `outgoing edges of id: ${callerInfo.id} must be in graph but can not be found, keep in slice to be sure`);
// lift baseEnv on the same level
const activeEnvironment = (0, built_in_function_definition_1.retrieveActiveEnvironment)(callerInfo.environment, baseEnvironment);
const name = callerInfo.name;
(0, assert_1.guard)(name !== undefined, () => `name of id: ${callerInfo.id} can not be found in id map`);
const functionCallDefs = (0, resolve_by_name_1.resolveByName)(name, activeEnvironment, identifier_1.ReferenceType.Unknown)?.filter(d => d.definedAt !== built_in_1.BuiltIn)?.map(d => d.nodeId) ?? [];
for (const [target, outgoingEdge] of outgoingEdges[1].entries()) {
if ((0, edge_1.edgeIncludesType)(outgoingEdge.types, edge_1.EdgeType.Calls)) {
functionCallDefs.push(target);
}
}
const functionCallTargets = queue.memoizeCallTargets(functionCallDefs.join(';'), () => (0, linker_1.getAllLinkedFunctionDefinitions)(new Set(functionCallDefs), dataflowGraph));
return [functionCallTargets, activeEnvironment];
}
function includeArgumentFunctionCallClosure(arg, baseEnvironment, activeEnvironment, queue, dataflowGraph) {
const valueRoot = (0, graph_1.getReferenceOfArgument)(arg);
if (!valueRoot) {
return;
}
const callTargets = queue.memoizeCallTargets(valueRoot, () => (0, linker_1.getAllLinkedFunctionDefinitions)(new Set([valueRoot]), dataflowGraph));
linkCallTargets(false, callTargets, activeEnvironment, (0, fingerprint_1.envFingerprint)(activeEnvironment), queue);
}
function linkCallTargets(onlyForSideEffects, functionCallTargets, activeEnvironment, activeEnvironmentFingerprint, queue) {
for (const functionCallTarget of functionCallTargets) {
for (const exitPoint of functionCallTarget.exitPoints) {
queue.add(exitPoint, activeEnvironment, activeEnvironmentFingerprint, onlyForSideEffects);
}
}
}
/** returns the new threshold hit count */
function sliceForCall(current, callerInfo, dataflowGraph, queue) {
const baseEnvironment = current.baseEnvironment;
const [functionCallTargets, activeEnvironment] = getAllFunctionCallTargets(dataflowGraph, callerInfo, current.baseEnvironment, queue);
const activeEnvironmentFingerprint = (0, fingerprint_1.envFingerprint)(activeEnvironment);
if (functionCallTargets.size === 0) {
/*
* if we do not have any call to resolve this function, we have to assume that every function passed is actually called!
* hence, we add a new flag and add all argument values to the queue causing directly
*/
for (const arg of callerInfo.args) {
includeArgumentFunctionCallClosure(arg, baseEnvironment, activeEnvironment, queue, dataflowGraph);
}
return;
}
linkCallTargets(current.onlyForSideEffects, functionCallTargets, activeEnvironment, activeEnvironmentFingerprint, queue);
}
/** Returns true if we found at least one return edge */
function handleReturns(from, queue, currentEdges, baseEnvFingerprint, baseEnvironment) {
const e = [...currentEdges.entries()];
const found = e.filter(([_, edge]) => (0, edge_1.edgeIncludesType)(edge.types, edge_1.EdgeType.Returns));
if (found.length === 0) {
return false;
}
for (const [target,] of found) {
queue.add(target, baseEnvironment, baseEnvFingerprint, false);
}
for (const [target, edge] of e) {
if ((0, edge_1.edgeIncludesType)(edge.types, edge_1.EdgeType.Reads)) {
queue.add(target, baseEnvironment, baseEnvFingerprint, false);
}
else if ((0, edge_1.edgeIncludesType)(edge.types, edge_1.EdgeType.DefinesOnCall | edge_1.EdgeType.DefinedByOnCall | edge_1.EdgeType.Argument)) {
(0, static_slicer_1.updatePotentialAddition)(queue, from, target, baseEnvironment, baseEnvFingerprint);
}
}
return true;
}
//# sourceMappingURL=slice-call.js.map