UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

139 lines 6.11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CallTargets = void 0; exports.satisfiesCallTargets = satisfiesCallTargets; exports.getValueOfArgument = getValueOfArgument; exports.identifyLinkToLastCallRelation = identifyLinkToLastCallRelation; 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 built_in_1 = require("../../../dataflow/environments/built-in"); const assert_1 = require("../../../util/assert"); const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type"); const r_function_call_1 = require("../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call"); const cascade_action_1 = require("./cascade-action"); 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 = {})); function satisfiesCallTargets(id, graph, callTarget) { const callVertex = graph.getVertex(id, true); if (callVertex === undefined || callVertex.tag !== vertex_1.VertexType.FunctionCall) { return 'no'; } const outgoing = graph.outgoingEdges(id); if (outgoing === undefined) { return 'no'; } const callTargets = [...outgoing] .filter(([, { types }]) => (0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.Calls)) .map(([t]) => t); let builtIn = false; if (callVertex.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)(callVertex.name, callVertex.environment, identifier_1.ReferenceType.Unknown); if (reResolved?.some(t => (0, built_in_1.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(built_in_1.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); } } function getValueOfArgument(graph, call, argument, additionalAllowedTypes) { if (!call) { return undefined; } const totalIndex = argument.name ? call.args.findIndex(arg => arg !== r_function_call_1.EmptyArgument && arg.name === argument.name) : -1; let refAtIndex; if (totalIndex < 0) { const references = call.args.filter(arg => arg !== r_function_call_1.EmptyArgument && !arg.name).map(graph_1.getReferenceOfArgument); refAtIndex = references[argument.index]; } else { const arg = call.args[totalIndex]; refAtIndex = (0, graph_1.getReferenceOfArgument)(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; } } function identifyLinkToLastCallRelation(from, cfg, graph, { callName, ignoreIf, cascadeIf }) { if (ignoreIf?.(from, graph)) { return []; } const found = []; (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 = graph.getVertex(node, true); if (vertex === undefined || vertex.tag !== vertex_1.VertexType.FunctionCall) { return; } if (callName.test(vertex.name)) { const act = cascadeIf ? cascadeIf(vertex, from, graph) : cascade_action_1.CascadeAction.Stop; if (act === cascade_action_1.CascadeAction.Skip) { return; } const tar = satisfiesCallTargets(vertex.id, graph, CallTargets.MustIncludeGlobal); if (tar === 'no') { return act === cascade_action_1.CascadeAction.Stop; } found.push(node); return act === cascade_action_1.CascadeAction.Stop; } }); return found; } //# sourceMappingURL=identify-link-to-last-call-relation.js.map