@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
156 lines • 6.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FlowrAnalyzerCache = void 0;
const flowr_cache_1 = require("./flowr-cache");
const default_pipelines_1 = require("../../core/steps/pipeline/default-pipelines");
const assert_1 = require("../../util/assert");
const flowr_analyzer_controlflow_cache_1 = require("./flowr-analyzer-controlflow-cache");
const call_graph_1 = require("../../dataflow/graph/call-graph");
/**
* This provides the full analyzer caching layer, please avoid using this directly
* and prefer the {@link FlowrAnalyzer}.
*/
class FlowrAnalyzerCache extends flowr_cache_1.FlowrCache {
args;
pipeline = undefined;
controlFlowCache = undefined;
callGraphCache = undefined;
constructor(args) {
super();
this.args = args;
this.initCacheProviders();
}
initCacheProviders() {
this.pipeline = (0, default_pipelines_1.createDataflowPipeline)(this.args.parser, {
context: this.args.context,
getId: this.args.getId
});
this.controlFlowCache = new flowr_analyzer_controlflow_cache_1.FlowrAnalyzerControlFlowCache();
this.callGraphCache = undefined;
}
static create(data) {
return new FlowrAnalyzerCache(data);
}
receive(event) {
super.receive(event);
switch (event.type) {
case "full" /* CacheInvalidationEventType.Full */:
this.initCacheProviders();
break;
default:
(0, assert_1.assertUnreachable)(event.type);
}
}
get() {
/* this will do a ref assignment, so indirect force */
return this.computeIfAbsent(false, () => this.pipeline.getResults(true));
}
reset() {
this.receive({ type: "full" /* CacheInvalidationEventType.Full */ });
}
async runTapeUntil(force, until) {
(0, assert_1.guard)(this.args.context.files.loadingOrder.getUnorderedRequests().length > 0, 'At least one request must be set to run the analysis pipeline');
if (force) {
this.reset();
}
let g;
while ((g = until()) === undefined && this.pipeline.hasNextStep()) {
await this.pipeline.nextStep();
}
(0, assert_1.guard)(g !== undefined, 'Could not reach the desired pipeline step, invalid cache state(?)');
return g;
}
/**
* Get the parse output for the request, parsing if necessary.
* @param force - Do not use the cache, instead force a new parse.
* @see {@link FlowrAnalyzerCache#peekParse} - to get the parse output if already available without triggering a new parse.
*/
async parse(force) {
const d = this.get();
return this.runTapeUntil(force, () => d.parse);
}
/**
* Get the parse output for the request if already available, otherwise return `undefined`.
* This will not trigger a new parse.
* @see {@link FlowrAnalyzerCache#parse} - to get the parse output, parsing if necessary.
*/
peekParse() {
return this.get().parse;
}
/**
* Get the normalized abstract syntax tree for the request, normalizing if necessary.
* @param force - Do not use the cache, instead force new analyses.
* @see {@link FlowrAnalyzerCache#peekNormalize} - to get the normalized AST if already available without triggering a new normalization.
*/
async normalize(force) {
const d = this.get();
return this.runTapeUntil(force, () => d.normalize);
}
/**
* Get the normalized abstract syntax tree for the request if already available, otherwise return `undefined`.
* This will not trigger a new normalization.
* @see {@link FlowrAnalyzerCache#normalize} - to get the normalized AST, normalizing if necessary.
*/
peekNormalize() {
return this.get().normalize;
}
/**
* Get the dataflow graph for the request, computing if necessary.
* @param force - Do not use the cache, instead force new analyses.
* @see {@link FlowrAnalyzerCache#peekDataflow} - to get the dataflow graph if already available without triggering a new computation.
*/
async dataflow(force) {
const d = this.get();
return this.runTapeUntil(force, () => d.dataflow);
}
/**
* Get the dataflow graph for the request if already available, otherwise return `undefined`.
* This will not trigger a new computation.
* @see {@link FlowrAnalyzerCache#dataflow} - to get the dataflow graph, computing if necessary.
*/
peekDataflow() {
return this.get().dataflow;
}
/**
* Get the control flow graph (CFG) for the request, computing if necessary.
* @param force - Do not use the cache, instead force new analyses.
* @param kind - The kind of CFG that is requested.
* @param simplifications - Simplification passes to be applied to the CFG.
*/
async controlflow(force, kind, simplifications) {
const cfgInfo = {
ctx: this.args.context,
cfgQuick: this.peekDataflow()?.cfgQuick,
ast: async () => await this.normalize(),
dfg: async () => await this.dataflow()
};
return this.controlFlowCache.get(force, kind, cfgInfo, simplifications);
}
/**
* Get the control flow graph (CFG) for the request if already available, otherwise return `undefined`.
* @param kind - The kind of CFG that is requested.
* @param simplifications - Simplification passes to be applied to the CFG.
* @see {@link FlowrAnalyzerCache#controlflow} - to get the control flow graph, computing if necessary.
*/
peekControlflow(kind, simplifications) {
return this.controlFlowCache.peek(kind, simplifications);
}
/**
* Get the call graph for the request, computing if necessary.
* @param force - Do not use the cache, instead force new analyses.
*/
async callGraph(force) {
if (!this.callGraphCache || force) {
this.callGraphCache = call_graph_1.CallGraph.compute((await this.dataflow(force)).graph);
}
return this.callGraphCache;
}
/**
* Get the call graph for the request if already available, otherwise return `undefined`.
*/
peekCallGraph() {
return this.callGraphCache;
}
}
exports.FlowrAnalyzerCache = FlowrAnalyzerCache;
//# sourceMappingURL=flowr-analyzer-cache.js.map