UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

162 lines 6.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FlowrAnalyzerLoadingOrderContext = void 0; const abstract_flowr_analyzer_context_1 = require("./abstract-flowr-analyzer-context"); const log_1 = require("../../util/log"); const arrays_1 = require("../../util/collections/arrays"); const flowr_analyzer_loading_order_plugin_1 = require("../plugins/loading-order-plugins/flowr-analyzer-loading-order-plugin"); const loadingOrderLog = log_1.log.getSubLogger({ name: 'loading-order' }); /** * This context is responsible for managing the loading order of script files in a project, including guesses and known orders provided by {@link FlowrAnalyzerLoadingOrderPlugin}s. * * If you are interested in inspecting these orders, refer to {@link ReadOnlyFlowrAnalyzerLoadingOrderContext}. * Plugins, however, can use this context directly to modify order guesses. */ class FlowrAnalyzerLoadingOrderContext extends abstract_flowr_analyzer_context_1.AbstractFlowrAnalyzerContext { name = 'flowr-analyzer-loading-order-context'; rerunRequired; constructor(ctx, plugins) { super(ctx, flowr_analyzer_loading_order_plugin_1.FlowrAnalyzerLoadingOrderPlugin.defaultPlugin(), plugins); this.rerunRequired = this.plugins.length > 0; } knownOrder; guesses = []; /** just the base collection of requests we know nothing about the order! */ unordered = []; /** what {@link unordered} already holds, so the same file is never analyzed (and reconstructed) twice */ seen = new Set(); reset() { this.knownOrder = undefined; this.guesses.length = 0; this.unordered.length = 0; this.seen.clear(); this.rerunRequired = this.plugins.length > 0; } /** * Registers `request` as unordered unless the same one is already there, reporting whether it was new. * Project discovery and the implicit-source scan reach the same file from both ends, so a file otherwise * lands in the loading order more than once and every later step repeats it. */ register(request) { const key = `${request.request}\u0000${request.content}`; if (this.seen.has(key)) { return false; } this.seen.add(key); this.unordered.push(request); return true; } /** * Add one or multiple requests to the context. * These are considered unordered (i.e., ordered implicitly by the order of addition) until a plugin provides either a guess or a known order. * * This is a batched version of {@link addRequest}. */ addRequests(requests) { if (!requests.map(r => this.register(r)).some(Boolean)) { return; } if (this.knownOrder || this.guesses.length > 0) { loadingOrderLog.warn(`Adding requests ${requests.map(r => r.request).join(', ')} after known order!`); this.rerunRequired = true; } } /** * Add a single request to the context. * This is considered unordered (i.e., ordered implicitly by the order of addition) until a plugin provides either a guess or a known order. * * If you want to add multiple requests, consider using {@link addRequests} instead for efficiency. */ addRequest(request) { if (!this.register(request)) { return; } if (this.knownOrder || this.guesses.length > 0) { loadingOrderLog.warn(`Adding request ${request.request} ${request.content} after known order!`); this.rerunRequired = true; } } /** * Add a guess for the loading order. This is mostly for plugins to use. * In case you have a certain order, use the `certain` flag to indicate this -- but please take care of *really* being certain! */ addGuess(guess, certain) { if (certain) { if (this.knownOrder) { loadingOrderLog.warn(`Adding certain guess ${guess.map(g => g.request).join(', ')} after known order!`); if (!(0, arrays_1.arrayEqual)(this.knownOrder, guess)) { loadingOrderLog.error(`Certain guess ${guess.map(g => g.request).join(', ')} does not match known order ${this.knownOrder.map(g => g.request).join(', ')}`); this.guesses.push(guess); } } else { this.knownOrder = guess; } } else { this.guesses.push(guess); } } /** * Move `requests` to the front of every order known so far, keeping their relative order. * Use this for files R evaluates before anything else (e.g. `.Rprofile`), as it refines the * existing orders instead of competing with them like {@link addGuess} would. */ prependToOrder(requests) { if (requests.length === 0) { return; } const front = (order) => [...requests, ...order.filter(r => !requests.includes(r))]; if (this.knownOrder) { this.knownOrder = front(this.knownOrder); } for (let i = 0; i < this.guesses.length; i++) { this.guesses[i] = front(this.guesses[i]); } if (!this.knownOrder && this.guesses.length === 0) { this.addGuess(front(this.unordered)); } } /** * Drop `requests` from every order known so far. Use this for files another file already * contains (e.g. the target of an Rmd `child` chunk), as it refines the existing orders instead * of competing with them like {@link addGuess} would. */ removeFromOrder(requests) { if (requests.length === 0) { return; } const drop = new Set(requests); const without = (order) => order.filter(r => !drop.has(r)); if (this.knownOrder) { this.knownOrder = without(this.knownOrder); } for (let i = 0; i < this.guesses.length; i++) { this.guesses[i] = without(this.guesses[i]); } if (!this.knownOrder && this.guesses.length === 0) { this.addGuess(without(this.unordered)); } } currentGuesses() { return this.guesses; } currentKnownOrder() { return this.knownOrder; } peekLoadingOrder() { return this.knownOrder ?? (this.guesses.length > 0 ? this.guesses[0] : undefined); } getUnorderedRequests() { return this.unordered; } getLoadingOrder() { if (this.rerunRequired) { this.rerunRequired = false; this.applyPlugins(undefined); } return this.peekLoadingOrder() ?? this.unordered; } } exports.FlowrAnalyzerLoadingOrderContext = FlowrAnalyzerLoadingOrderContext; //# sourceMappingURL=flowr-analyzer-loading-order-context.js.map