UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

167 lines 7.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CallTargets = void 0; exports.satisfiesCallTargets = satisfiesCallTargets; exports.getValueOfArgument = getValueOfArgument; exports.identifyLinkToLastCallRelation = identifyLinkToLastCallRelation; exports.identifyLinkToLastCallRelationSync = identifyLinkToLastCallRelationSync; const node_id_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/node-id"); const graph_1 = require("../../../dataflow/graph/graph"); const simple_visitor_1 = require("../../../control-flow/simple-visitor"); const vertex_1 = require("../../../dataflow/graph/vertex"); const edge_1 = require("../../../dataflow/graph/edge"); const resolve_by_name_1 = require("../../../dataflow/environments/resolve-by-name"); const identifier_1 = require("../../../dataflow/environments/identifier"); const assert_1 = require("../../../util/assert"); const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type"); const cascade_action_1 = require("./cascade-action"); const cfg_kind_1 = require("../../../project/cfg-kind"); var CallTargets; (function (CallTargets) { /** call targets a function that is not defined locally in the script (e.g., the call targets a library function) */ CallTargets["OnlyGlobal"] = "global"; /** call targets a function that is defined locally or globally, but must include a global function */ CallTargets["MustIncludeGlobal"] = "must-include-global"; /** call targets a function that is defined locally */ CallTargets["OnlyLocal"] = "local"; /** call targets a function that is defined locally or globally, but must include a local function */ CallTargets["MustIncludeLocal"] = "must-include-local"; /** call targets a function that is defined locally or globally */ CallTargets["Any"] = "any"; })(CallTargets || (exports.CallTargets = CallTargets = {})); /** * Determines whether the given function call node satisfies the specified call target condition. */ function satisfiesCallTargets(info, graph, callTarget) { const outgoing = graph.outgoingEdges(info.id); if (outgoing === undefined) { return 'no'; } const callTargets = outgoing.entries() .filter(([, e]) => edge_1.DfEdge.includesType(e, edge_1.EdgeType.Calls)) .map(([t]) => t) .toArray(); let builtIn = false; if (info.environment !== undefined) { /* * for performance and scoping reasons, flowR will not identify the global linkage, * including any potential built-in mapping. */ const reResolved = (0, resolve_by_name_1.resolveByName)(info.name, info.environment, identifier_1.ReferenceType.Unknown); if (reResolved?.some(t => node_id_1.NodeId.isBuiltIn(t.definedAt))) { builtIn = true; } } else { /* if we have a call with an unbound environment, * this only happens if we are sure of built-in relations and want to save references */ builtIn = true; } switch (callTarget) { case CallTargets.Any: return callTargets; case CallTargets.OnlyGlobal: if (callTargets.every(node_id_1.NodeId.isBuiltIn)) { return builtIn ? ['built-in'] : []; } else { return 'no'; } case CallTargets.MustIncludeGlobal: return builtIn || callTargets.length === 0 ? [...callTargets, 'built-in'] : 'no'; case CallTargets.OnlyLocal: return !builtIn && callTargets.length > 0 ? callTargets : 'no'; case CallTargets.MustIncludeLocal: if (callTargets.length > 0) { return builtIn ? [...callTargets, 'built-in'] : callTargets; } else { return 'no'; } default: (0, assert_1.assertUnreachable)(callTarget); } } /** * Gets the value node of the specified argument in the given function call, if it exists and matches the allowed types. */ function getValueOfArgument(graph, call, argument, additionalAllowedTypes) { if (!call) { return undefined; } const totalIndex = argument.name ? call.args.findIndex(arg => graph_1.FunctionArgument.hasName(arg, argument.name)) : -1; let refAtIndex; if (totalIndex < 0) { const references = call.args.filter(graph_1.FunctionArgument.isPositional).map(graph_1.FunctionArgument.getReference); refAtIndex = references[argument.index]; } else { const arg = call.args[totalIndex]; refAtIndex = graph_1.FunctionArgument.getReference(arg); } if (refAtIndex === undefined) { return undefined; } let valueNode = graph.idMap?.get(refAtIndex); if (valueNode?.type === type_1.RType.Argument) { valueNode = valueNode.value; } if (valueNode) { return !additionalAllowedTypes || additionalAllowedTypes.includes(valueNode.type) ? valueNode : undefined; } } /** * **Please refer to {@link identifyLinkToRelation}.** * * Identifies nodes that link to the last call of a specified function from a given starting node in the control flow graph. * If you pass on `knownCalls` (e.g., produced by {@link getCallsInCfg}), this will only respect the functions * listed there and ignore any other calls. This can be also used to speed up the process if you already have * the known calls available. * @see {@link identifyLinkToLastCallRelationSync} for the synchronous version. */ async function identifyLinkToLastCallRelation(from, analyzer, l, knownCalls) { const graph = (await analyzer.dataflow()).graph; const cfg = (await analyzer.controlflow([], cfg_kind_1.CfgKind.WithDataflow)).graph; return identifyLinkToLastCallRelationSync(from, cfg, graph, l, knownCalls); } /** * Synchronous version of {@link identifyLinkToLastCallRelation}. */ function identifyLinkToLastCallRelationSync(from, cfg, graph, { callName, cascadeIf, ignoreIf }, knownCalls) { if (ignoreIf?.(from, graph)) { return []; } const found = []; const cNameCheck = callName instanceof RegExp ? ({ name }) => callName.test(identifier_1.Identifier.getName(name)) : ({ name }) => callName(identifier_1.Identifier.getName(name)); const getVertex = knownCalls ? (node) => knownCalls.get(node) : (node) => { const v = graph.getVertex(node); return (0, vertex_1.isFunctionCallVertex)(v) ? v : undefined; }; (0, simple_visitor_1.visitCfgInReverseOrder)(cfg, [from], node => { /* we ignore the start id as it cannot be the last call */ if (node === from) { return; } const vertex = getVertex(node); if (vertex === undefined) { return; } if (cNameCheck(vertex)) { const act = cascadeIf ? cascadeIf(vertex, from, graph) : cascade_action_1.CascadeAction.Stop; if (act === cascade_action_1.CascadeAction.Skip) { return; } const tar = satisfiesCallTargets(vertex, graph, CallTargets.MustIncludeGlobal); if (tar !== 'no') { found.push(node); } return act === cascade_action_1.CascadeAction.Stop; } }); return found; } //# sourceMappingURL=identify-link-to-last-call-relation.js.map