@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
94 lines • 4.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SourceInlineMap = void 0;
const vertex_1 = require("../../dataflow/graph/vertex");
const built_in_proc_name_1 = require("../../dataflow/environments/built-in-proc-name");
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");
/** the string-literal path argument of a `source()`-like call node, if it has one (else `undefined`) */
function sourceArg(ast, callId) {
const node = ast.idMap.get(callId);
const arg = node?.type === type_1.RType.FunctionCall ? node.arguments[0] : undefined;
return arg !== undefined && arg !== r_function_call_1.EmptyArgument && arg.value?.type === type_1.RType.String ? arg.value.content.str : undefined;
}
/**
* Helper functions to link `source()` calls to the files they source for reconstruction inlining.
* @see {@link SourceInlineMap.build}
*/
exports.SourceInlineMap = {
name: 'SourceInlineMap',
/**
* Connect each `source()` call node to the index of the file it sources in `ast.ast.files` (index 0 is main).
*
* A `source()` call lives in its parent file, so we reach its sourced block via the control dependency the
* dataflow analysis adds (see {@link sourceRequest}): a vertex whose innermost cd (`cds[0]`) is a source call
* belongs to that call's directly sourced block. Every node carries its resolved file in `info.file`, so the
* block's `info.file` names the sourced file independent of the call site, and one graph pass resolves all
* calls via small map lookups. A file sourced from several sites is stored once but each block still tags the
* same `info.file`, so all sites map to that single index (this also drives cycle detection: a re-sourcing
* call resolves to an already-visited index).
* @param ast - the normalized (multi-file) ast
* @param graph - the dataflow graph for `ast`
* @returns a map from each `source()` call node id to the index of the sourced file in `ast.ast.files`
*/
build(ast, graph) {
const files = ast.ast.files;
const map = new Map();
if (files.length <= 1) {
return map;
}
// resolved file path -> its index (paths are unique per stored file)
const pathToIndex = new Map();
for (let i = 1; i < files.length; i++) {
const path = files[i].root.info.file;
if (path !== undefined) {
pathToIndex.set(path, i);
}
}
// single pass: collect every source call (with its literal path arg, computed once) and, per control
// dependency, the first vertex under it (its directly sourced block).
const sourceArgs = new Map();
const firstVertexByCd = new Map();
for (const [id, vertex] of graph.vertices(true)) {
if (vertex_1.FunctionCallVertex.is(vertex) && Array.isArray(vertex.origin) && vertex.origin.includes(built_in_proc_name_1.BuiltInProcName.Source)) {
sourceArgs.set(id, sourceArg(ast, id));
}
const cds = vertex.cds;
if (cds && cds.length > 0 && !firstVertexByCd.has(cds[0].id)) {
firstVertexByCd.set(cds[0].id, id);
}
}
if (sourceArgs.size === 0) {
return map;
}
// resolve each call via the `info.file` of the block it directly sources (if it has one)
for (const callId of sourceArgs.keys()) {
const block = firstVertexByCd.get(callId);
const path = block !== undefined ? ast.idMap.get(block)?.info.file : undefined;
const idx = path !== undefined ? pathToIndex.get(path) : undefined;
if (idx !== undefined) {
map.set(callId, idx);
}
}
// a re-source of an already-stored file (a diamond's shared leaf, or a cycle edge) is deduplicated and
// has no block of its own, so the pass above misses it. Map it by its literal path arg, which another
// (resolved) call to the same file already fixed.
const argToIndex = new Map();
for (const [callId, idx] of map) {
const arg = sourceArgs.get(callId);
if (arg !== undefined) {
argToIndex.set(arg, idx);
}
}
for (const [callId, arg] of sourceArgs) {
if (arg !== undefined && !map.has(callId)) {
const idx = argToIndex.get(arg);
if (idx !== undefined) {
map.set(callId, idx);
}
}
}
return map;
}
};
//# sourceMappingURL=source-inline-map.js.map