UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

109 lines 4.75 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.VisitingQueue = void 0; const fingerprint_1 = require("./fingerprint"); const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id"); const gas_1 = require("../../gas"); const static_slicer_1 = require("./static-slicer"); /** How many nodes the traversal visits between two {@link GasFeatureKey.Slicer|gas} checks. */ const GasCheckEvery = 512; class VisitingQueue { threshold; timesHitThreshold = 0; seen = new Map(); seenByCache = new Set(); idThreshold = new Map(); queue = []; cache = new Map(); // the set of potential additions holds nodes which may be added if a second edge deems them relevant (e.g., found with the `defined-by-on-call` edge) // additionally it holds which node id added the addition so we can separate their inclusion on the structure potentialAdditions = new Map(); cachedCallTargets = new Map(); /** whether the dataflow graph has a vertex for an id, i.e. whether the traversal could continue from it */ isGraphVertex; gas; stoppedEarly = false; untilGasCheck = 0; /** entries dequeued, see {@link SliceProgress} */ visited = 0; constructor(threshold, cache, isGraphVertex, gas) { this.threshold = threshold; this.cache = cache; this.isGraphVertex = isGraphVertex; this.gas = gas; } /** * Adds a node to the queue if it has not been seen before. * @param target - the node to add * @param env - the environment the node is traversed in * @param envFingerprint - the fingerprint of the environment * @param onlyForSideEffects - whether the node is only used for its side effects */ add(target, env, envFingerprint, onlyForSideEffects) { /* a built-in without a vertex is R's own definition (`x <- 1` reads `built-in:<-`), a dead end the traversal * would drop right back out of, so it only ever widened the result */ if (node_id_1.NodeId.isBuiltIn(target) && this.isGraphVertex?.(target) === false) { return; } const idCounter = this.idThreshold.get(target) ?? 0; if (idCounter > this.threshold) { this.timesHitThreshold++; return; } /* we do not include the in call part in the fingerprint as it is 'deterministic' from the source position */ const print = (0, fingerprint_1.fingerprint)(target, envFingerprint, onlyForSideEffects); if (!this.seen.has(print)) { const cached = this.cache?.get(print); if (cached) { this.seenByCache.add(target); for (const id of cached) { this.queue.push({ id, baseEnvironment: env, envFingerprint, onlyForSideEffects }); } } this.idThreshold.set(target, idCounter + 1); this.seen.set(print, target); this.queue.push({ id: target, baseEnvironment: env, envFingerprint, onlyForSideEffects }); } } next() { this.visited++; return this.queue.pop(); } /** Whether there is anything left to visit, which the traversal is out of gas for as soon as it is exhausted. */ nonEmpty() { return this.queue.length > 0 && !this.outOfGas(); } /** * The traversal is synchronous, so a caller can only bound it from within. Gas is polled every * {@link GasCheckEvery} nodes, which keeps even an enabled check off the per-node path. */ outOfGas() { if (this.gas === undefined || this.untilGasCheck-- > 0) { return this.stoppedEarly; } this.untilGasCheck = GasCheckEvery - 1; if (!this.stoppedEarly && this.gas.checkGas(gas_1.GasFeatureKey.Slicer) >= 2 /* GasLevel.Critical */) { this.stoppedEarly = true; static_slicer_1.slicerLogger.warn(`slicing ran out of gas, the slice is incomplete (${gas_1.GasWikiRef})`); } return this.stoppedEarly; } hasId(id) { return this.idThreshold.has(id); } memoizeCallTargets(id, targets) { if (!this.cachedCallTargets.has(id)) { this.cachedCallTargets.set(id, targets()); } return this.cachedCallTargets.get(id); } status() { return { timesHitThreshold: this.timesHitThreshold, result: new Set([...this.seen.values(), ...this.seenByCache]), ...(this.stoppedEarly ? { stoppedEarly: true, progress: { visited: this.visited, frontier: this.queue.length } } : {}) }; } } exports.VisitingQueue = VisitingQueue; //# sourceMappingURL=visiting-queue.js.map