UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

98 lines 5.76 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAllFunctionCallTargetsForSlice = getAllFunctionCallTargetsForSlice; 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 resolve_by_name_1 = require("../../dataflow/environments/resolve-by-name"); const edge_1 = require("../../dataflow/graph/edge"); const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id"); 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 getAllFunctionCallTargetsForSlice(dataflowGraph, callerInfo, baseEnvironment, queue, ctx) { // 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, ctx); 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 => !node_id_1.NodeId.isBuiltIn(d.definedAt))?.map(d => d.nodeId) ?? []; for (const [target, outgoingEdge] of outgoingEdges[1].entries()) { if (edge_1.DfEdge.includesType(outgoingEdge, edge_1.EdgeType.Calls)) { functionCallDefs.push(target); } } const functionCallTargets = queue.memoizeCallTargets(functionCallDefs.join(';'), () => (0, linker_1.getAllLinkedFunctionDefinitions)(new Set(functionCallDefs), dataflowGraph)[0]); return [functionCallTargets, activeEnvironment]; } function includeArgumentFunctionCallClosure(arg, activeEnvironment, queue, dataflowGraph) { const valueRoot = graph_1.FunctionArgument.getReference(arg); if (!valueRoot) { return; } const callTargets = queue.memoizeCallTargets(valueRoot, () => (0, linker_1.getAllLinkedFunctionDefinitions)(new Set([valueRoot]), dataflowGraph)[0]); 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.nodeId, activeEnvironment, activeEnvironmentFingerprint, onlyForSideEffects); } // handle open reads for (const openIn of functionCallTarget.subflow.in) { // resolve them in the active env if (openIn.name) { const resolved = (0, resolve_by_name_1.resolveByName)(openIn.name, activeEnvironment, identifier_1.ReferenceType.Unknown); for (const res of resolved ?? []) { (0, static_slicer_1.updatePotentialAddition)(queue, functionCallTarget.id, res.nodeId, activeEnvironment, activeEnvironmentFingerprint); } } } } } /** returns the new threshold hit count */ function sliceForCall(current, callerInfo, { graph }, queue, ctx) { const [functionCallTargets, activeEnvironment] = getAllFunctionCallTargetsForSlice(graph, callerInfo, current.baseEnvironment, queue, ctx); 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, activeEnvironment, queue, graph); } return; } const activeEnvironmentFingerprint = (0, fingerprint_1.envFingerprint)(activeEnvironment); linkCallTargets(current.onlyForSideEffects, functionCallTargets, activeEnvironment, activeEnvironmentFingerprint, queue); } const PotentialFollowOnReturn = edge_1.EdgeType.DefinesOnCall | edge_1.EdgeType.DefinedByOnCall | edge_1.EdgeType.Argument; /** Returns true if we found at least one return edge */ function handleReturns(from, queue, currentEdges, baseEnvFingerprint, baseEnvironment) { const e = Array.from(currentEdges.entries()); const found = e.filter(([_, edge]) => edge_1.DfEdge.includesType(edge, 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 (edge_1.DfEdge.includesType(edge, edge_1.EdgeType.Reads)) { queue.add(target, baseEnvironment, baseEnvFingerprint, false); } else if (edge_1.DfEdge.includesType(edge, PotentialFollowOnReturn)) { (0, static_slicer_1.updatePotentialAddition)(queue, from, target, baseEnvironment, baseEnvFingerprint); } } return true; } //# sourceMappingURL=slice-call.js.map