@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
136 lines • 5.98 kB
JavaScript
;
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 visitor_1 = require("../../../util/cfg/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 (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.get(id);
if (callVertex === undefined || callVertex[0].tag !== vertex_1.VertexType.FunctionCall) {
return 'no';
}
const [info, outgoing] = callVertex;
const callTargets = [...outgoing]
.filter(([, e]) => (0, edge_1.edgeIncludesType)(e.types, edge_1.EdgeType.Calls))
.map(([t]) => t);
let builtIn = false;
if (info.environment === undefined) {
/* 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;
}
else {
/*
* 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 => t.definedAt === built_in_1.BuiltIn)) {
builtIn = true;
}
}
switch (callTarget) {
case CallTargets.Any:
return callTargets;
case CallTargets.OnlyGlobal:
if (callTargets.length === 0) {
return builtIn ? [built_in_1.BuiltIn] : [];
}
else {
return 'no';
}
case CallTargets.MustIncludeGlobal:
return builtIn || callTargets.length === 0 ? [...callTargets, built_in_1.BuiltIn] : 'no';
case CallTargets.OnlyLocal:
return !builtIn && callTargets.length > 0 ? callTargets : 'no';
case CallTargets.MustIncludeLocal:
if (callTargets.length > 0) {
return builtIn ? [...callTargets, built_in_1.BuiltIn] : 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 }) {
const found = [];
if (ignoreIf && ignoreIf(from, graph)) {
return found;
}
(0, 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.get(node);
if (vertex === undefined || vertex[0].tag !== vertex_1.VertexType.FunctionCall) {
return;
}
if (callName.test(vertex[0].name)) {
const act = cascadeIf ? cascadeIf(vertex[0], from, graph) : cascade_action_1.CascadeAction.Stop;
if (act === cascade_action_1.CascadeAction.Skip) {
return;
}
const tar = satisfiesCallTargets(vertex[0].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