UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

236 lines 9.44 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FlowrAnalyzerFilesContext = void 0; const abstract_flowr_analyzer_context_1 = require("./abstract-flowr-analyzer-context"); const retriever_1 = require("../../r-bridge/retriever"); const assert_1 = require("../../util/assert"); const flowr_analyzer_project_discovery_plugin_1 = require("../plugins/project-discovery/flowr-analyzer-project-discovery-plugin"); const flowr_analyzer_file_plugin_1 = require("../plugins/file-plugins/flowr-analyzer-file-plugin"); const flowr_file_1 = require("./flowr-file"); const log_1 = require("../../util/log"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const fileLog = log_1.log.getSubLogger({ name: 'flowr-analyzer-files-context' }); function wrapFile(file, roles) { if (typeof file === 'string') { return new flowr_file_1.FlowrTextFile(file, roles); } else if ('request' in file) { return flowr_file_1.FlowrFile.fromRequest(file); } else { return file; } } /** * This is the analyzer file context to be modified by all plugins that affect the files. * If you are interested in inspecting these files, refer to {@link ReadOnlyFlowrAnalyzerFilesContext}. * Plugins, however, can use this context directly to modify files. */ class FlowrAnalyzerFilesContext extends abstract_flowr_analyzer_context_1.AbstractFlowrAnalyzerContext { name = 'flowr-analyzer-files-context'; loadingOrder; /* all project files etc., this contains *all* (non-inline) files, loading orders etc. are to be handled by plugins */ files = new Map(); inlineFiles = []; fileLoaders; /** these are all the paths of files that have been considered by the dataflow graph (even if not added) */ consideredFiles = []; /* files that are part of the analysis, e.g. source files */ byRole = Object.fromEntries(Object.values(flowr_file_1.FileRole).map(k => [k, []])); constructor(loadingOrder, plugins, fileLoaders) { super(loadingOrder.getAttachedContext(), flowr_analyzer_project_discovery_plugin_1.FlowrAnalyzerProjectDiscoveryPlugin.defaultPlugin(), plugins); this.fileLoaders = [...fileLoaders, flowr_analyzer_file_plugin_1.FlowrAnalyzerFilePlugin.defaultPlugin()]; this.loadingOrder = loadingOrder; } reset() { this.loadingOrder.reset(); this.files = new Map(); this.consideredFiles.length = 0; this.inlineFiles.length = 0; this.byRole = Object.fromEntries(Object.values(flowr_file_1.FileRole).map(k => [k, []])); } /** * Record that a file has been considered during dataflow analysis. */ addConsideredFile(path) { this.consideredFiles.push(path); } /** * Get all files that have been considered during dataflow analysis. */ consideredFilesList() { return this.consideredFiles; } /** * Add multiple requests to the context. This is just a convenience method that calls {@link addRequest} for each request. */ addRequests(requests) { for (const request of requests) { this.addRequest(request); } } /** * Add a request to the context. If the request is of type `project`, it will be expanded using the registered {@link FlowrAnalyzerProjectDiscoveryPlugin}s. */ addRequest(request) { if (request.request !== 'project') { this.loadingOrder.addRequest(request); return; } const expandedRequests = this.applyPlugins(request).flat(); for (const req of expandedRequests) { if ((0, retriever_1.isParseRequest)(req)) { this.addRequest(req); } else { this.addFile(req, req.roles); } } } /** * Add multiple files to the context. This is just a convenience method that calls {@link addFile} for each file. */ addFiles(files) { for (const file of files) { this.addFile(file); } } /** * Add a file to the context. If the file has a special role, it will be added to the corresponding list of special files. * This method also applies any registered {@link FlowrAnalyzerFilePlugin}s to the file before adding it to the context. */ addFile(file, roles) { const f = this.fileLoadPlugins(wrapFile(file, roles)); if (f.path() === flowr_file_1.FlowrFile.INLINE_PATH) { this.inlineFiles.push(f); } else { const exist = this.files.get(f.path()); (0, assert_1.guard)(exist === undefined || exist === f, `File ${f.path()} already added to the context.`); this.files.set(f.path(), f); } if (f.roles) { for (const r of f.roles) { this.byRole[r].push(f); } } return f; } hasFile(path) { return this.files.has(path) || (this.ctx.config.project.resolveUnknownPathsOnDisk && fs_1.default.existsSync(path)); } exists(p, ignoreCase) { try { if (!ignoreCase) { return this.hasFile(p) ? p : undefined; } if (this.hasFile(p)) { return p; } // walk the directory and find the first match const dir = path_1.default.dirname(p); const file = path_1.default.basename(p).toLowerCase(); // try to find in local known files first for (const f of this.files.keys()) { if (path_1.default.dirname(f).toLowerCase() !== dir.toLowerCase()) { continue; } const lf = path_1.default.basename(f).toLowerCase(); if (file === lf) { return f; } } if (this.ctx.config.project.resolveUnknownPathsOnDisk) { let files; if (fs_1.default.existsSync(dir)) { files = fs_1.default.readdirSync(dir); } else { // try to find a dir in parent const parentDir = path_1.default.dirname(dir); if (fs_1.default.existsSync(parentDir)) { const parentFiles = fs_1.default.readdirSync(parentDir); const foundDir = parentFiles.find(f => f.toLowerCase() === path_1.default.basename(dir).toLowerCase()); if (foundDir) { files = fs_1.default.readdirSync(path_1.default.join(parentDir, foundDir)); } } } const found = files?.find(f => f.toLowerCase() === file); return found ? path_1.default.join(dir, found) : undefined; } return undefined; } catch { return undefined; } } fileLoadPlugins(f) { let fFinal = f; for (const loader of this.fileLoaders) { if (loader.applies(f.path())) { fileLog.debug(`Applying file loader ${loader.name} to file ${f.path()}`); const res = loader.processor(this.ctx, fFinal); if (Array.isArray(res)) { fFinal = res[0]; if (!res[1]) { break; } } else { fFinal = res; break; } } } return fFinal; } resolveRequest(r) { if (r.request === 'text') { return { r }; } const file = this.files.get(r.content); if (file === undefined && this.ctx.config.project.resolveUnknownPathsOnDisk) { fileLog.debug(`File ${r.content} not found in context, trying to load from disk.`); if (fs_1.default.existsSync(r.content)) { const loadedFile = this.addFile(new flowr_file_1.FlowrTextFile(r.content)); return { r: { request: 'text', content: loadedFile.content().toString(), }, path: loadedFile.path() }; } } (0, assert_1.guard)(file !== undefined && file !== null, `File ${r.content} not found in context.`); const content = file.content(); return { r: { request: 'text', content: typeof content === 'string' ? content : '', }, path: file.path() }; } /** * Get all requests that have been added to this context. * This is a convenience method that calls {@link FlowrAnalyzerLoadingOrderContext.getLoadingOrder}. */ computeLoadingOrder() { return this.loadingOrder.getLoadingOrder(); } getFilesByRole(role) { return this.byRole[role]; } getAllFiles() { return [...this.files.values(), ...this.inlineFiles]; } } exports.FlowrAnalyzerFilesContext = FlowrAnalyzerFilesContext; //# sourceMappingURL=flowr-analyzer-files-context.js.map