@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
158 lines • 7.69 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Enrichments = exports.Enrichment = void 0;
exports.enrichmentContent = enrichmentContent;
exports.enrichElement = enrichElement;
const objects_1 = require("../../util/objects");
const vertex_1 = require("../../dataflow/graph/vertex");
const assert_1 = require("../../util/assert");
const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id");
const cfg_simplification_1 = require("../../control-flow/cfg-simplification");
const call_context_query_executor_1 = require("../../queries/catalog/call-context-query/call-context-query-executor");
const cfg_kind_1 = require("../../project/cfg-kind");
const identify_link_to_last_call_relation_1 = require("../../queries/catalog/call-context-query/identify-link-to-last-call-relation");
const identifier_1 = require("../../dataflow/environments/identifier");
const df_helper_1 = require("../../dataflow/graph/df-helper");
/**
* An enumeration that stores the names of the available enrichments that can be applied to a set of search elements.
* See {@link FlowrSearchBuilder.with} for more information on how to apply enrichments.
*/
var Enrichment;
(function (Enrichment) {
Enrichment["CallTargets"] = "call-targets";
Enrichment["LastCall"] = "last-call";
Enrichment["CfgInformation"] = "cfg-information";
Enrichment["QueryData"] = "query-data";
})(Enrichment || (exports.Enrichment = Enrichment = {}));
/**
* The registry of enrichments that are currently supported by the search.
* See {@link FlowrSearchBuilder.with} for more information on how to apply enrichments.
*/
exports.Enrichments = {
[Enrichment.CallTargets]: {
enrichElement: async (e, _s, analyzer, args, prev) => {
// we don't resolve aliases here yet!
const content = { targets: [] };
const df = await analyzer.dataflow();
const n = await analyzer.normalize();
const callVertex = df.graph.getVertex(e.node.info.id);
if (callVertex?.tag === vertex_1.VertexType.FunctionCall) {
const origins = df_helper_1.Dataflow.origin(df.graph, callVertex.id);
if (!origins || origins.length === 0) {
content.targets = [(0, node_id_1.recoverName)(callVertex.id, n.idMap)];
}
else {
// find call targets in user code (which have ids!)
content.targets = content.targets.concat(origins.map(o => {
switch (o.type) {
case 2 /* OriginType.FunctionCallOrigin */:
return {
node: n.idMap.get(o.id),
};
case 3 /* OriginType.BuiltInFunctionOrigin */:
return identifier_1.Identifier.toString(o.fn.name);
default:
return undefined;
}
}).filter(assert_1.isNotUndefined));
if (content.targets.length === 0) {
content.targets = [(0, node_id_1.recoverName)(callVertex.id, n.idMap)];
}
}
}
// if there is a call target that is not built-in (ie a custom function), we don't want to include it here
if (args?.onlyBuiltin && content.targets.some(t => typeof t !== 'string')) {
content.targets = [];
}
if (prev) {
content.targets.push(...prev.targets);
}
return content;
},
// as built-in call target enrichments are not nodes, we don't return them as part of the mapper!
mapper: ({ targets }) => targets.map(t => t).filter(t => t.node !== undefined)
},
[Enrichment.LastCall]: {
enrichElement: async (e, _s, analyzer, args, prev) => {
(0, assert_1.guard)(args && args.length, `${Enrichment.LastCall} enrichment requires at least one argument`);
const content = prev ?? { linkedIds: [] };
const df = (await analyzer.dataflow()).graph;
const vertex = df.getVertex(e.node.info.id);
if (vertex?.tag === vertex_1.VertexType.FunctionCall) {
const n = await analyzer.normalize();
const cfg = (await analyzer.controlflow(undefined, cfg_kind_1.CfgKind.Quick)).graph;
for (const arg of args) {
const lastCalls = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelationSync)(vertex.id, cfg, df, {
...arg,
callName: (0, call_context_query_executor_1.promoteCallName)(arg.callName),
type: 'link-to-last-call',
});
for (const lastCall of lastCalls) {
content.linkedIds.push({ node: n.idMap.get(lastCall) });
}
}
}
return content;
},
mapper: ({ linkedIds }) => linkedIds
},
[Enrichment.CfgInformation]: {
enrichElement: (e, search, _data, _args, prev) => {
const searchContent = search.enrichmentContent(Enrichment.CfgInformation);
return {
...prev,
isRoot: searchContent.cfg.graph.rootIds().has(e.node.info.id),
isReachable: searchContent.reachableNodes?.has(e.node.info.id)
};
},
enrichSearch: async (_search, data, args, prev) => {
args = {
forceRefresh: false,
checkReachable: false,
simplificationPasses: cfg_simplification_1.DefaultCfgSimplificationOrder,
...args
};
// short-circuit if we already have a cfg stored
if (!args.forceRefresh && prev?.simpleCfg) {
return prev;
}
const content = {
...prev,
cfg: await data.controlflow(args.simplificationPasses, cfg_kind_1.CfgKind.WithDataflow),
};
if (args.checkReachable) {
content.reachableNodes = (0, cfg_simplification_1.cfgFindAllReachable)(content.cfg);
}
return content;
}
},
[Enrichment.QueryData]: {
// the query data enrichment is just a "pass-through" that passes the query data to the underlying search
enrichElement: (_e, _search, _data, args, prev) => (args ?? prev),
enrichSearch: (_search, _data, args, prev) => (0, objects_1.deepMergeObject)(prev, args)
}
};
/**
* Returns the content of the given enrichment type from a {@link FlowrSearchElement}.
* If the search element is not enriched with the given enrichment, `undefined` is returned.
* @param e - The search element whose enrichment content should be retrieved.
* @param enrichment - The enrichment content, if present, else `undefined`.
*/
function enrichmentContent(e, enrichment) {
return e?.enrichments?.[enrichment];
}
/**
* Enriches the given search element with the given enrichment type, using the provided analysis data.
*/
async function enrichElement(e, s, analyzer, enrichment, args) {
const enrichmentData = exports.Enrichments[enrichment];
const prev = e?.enrichments;
return {
...e,
enrichments: {
...prev ?? {},
[enrichment]: await enrichmentData.enrichElement?.(e, s, analyzer, args, prev?.[enrichment])
}
};
}
//# sourceMappingURL=search-enrichers.js.map