UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

94 lines 4.76 kB
"use strict"; 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