@eagleoutice/flowr 2.6.0 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -40
- package/abstract-interpretation/data-frame/absint-visitor.d.ts +1 -1
- package/abstract-interpretation/data-frame/absint-visitor.js +1 -1
- package/cli/flowr.js +8 -4
- package/cli/repl/commands/repl-cfg.js +4 -4
- package/cli/repl/commands/repl-commands.js +2 -2
- package/cli/repl/commands/repl-dataflow.js +4 -4
- package/cli/repl/commands/repl-execute.d.ts +1 -1
- package/cli/repl/commands/repl-execute.js +8 -8
- package/cli/repl/commands/repl-lineage.d.ts +2 -2
- package/cli/repl/commands/repl-lineage.js +11 -12
- package/cli/repl/commands/repl-main.d.ts +4 -7
- package/cli/repl/commands/repl-normalize.js +2 -2
- package/cli/repl/commands/repl-parse.js +3 -2
- package/cli/repl/commands/repl-query.js +3 -3
- package/cli/repl/commands/repl-quit.js +1 -1
- package/cli/repl/commands/repl-version.d.ts +1 -16
- package/cli/repl/commands/repl-version.js +2 -19
- package/cli/repl/core.d.ts +9 -9
- package/cli/repl/core.js +14 -20
- package/cli/repl/print-version.js +2 -2
- package/cli/repl/server/connection.js +6 -2
- package/cli/repl/server/messages/message-hello.d.ts +1 -1
- package/cli/repl/server/server.js +2 -2
- package/core/steps/all/core/20-dataflow.d.ts +3 -1
- package/core/steps/pipeline/default-pipelines.d.ts +66 -50
- package/dataflow/environments/default-builtin-config.js +8 -0
- package/dataflow/eval/resolve/alias-tracking.js +2 -0
- package/dataflow/eval/resolve/resolve.js +3 -0
- package/dataflow/eval/values/r-value.d.ts +4 -1
- package/dataflow/eval/values/r-value.js +2 -0
- package/dataflow/extractor.d.ts +4 -1
- package/dataflow/extractor.js +7 -5
- package/dataflow/fn/higher-order-function.d.ts +9 -0
- package/dataflow/fn/higher-order-function.js +75 -0
- package/documentation/doc-util/doc-repl.js +5 -2
- package/documentation/print-dataflow-graph-wiki.js +2 -2
- package/documentation/print-query-wiki.js +20 -0
- package/documentation/print-readme.js +1 -1
- package/package.json +1 -1
- package/project/cache/flowr-analyzer-cache.d.ts +6 -5
- package/project/cache/flowr-analyzer-cache.js +21 -13
- package/project/cfg-kind.d.ts +17 -0
- package/project/cfg-kind.js +22 -0
- package/project/context/abstract-flowr-analyzer-context.d.ts +4 -0
- package/project/context/flowr-analyzer-context.d.ts +6 -0
- package/project/context/flowr-analyzer-context.js +11 -0
- package/project/context/flowr-analyzer-dependencies-context.d.ts +1 -0
- package/project/context/flowr-analyzer-dependencies-context.js +4 -0
- package/project/context/flowr-analyzer-files-context.d.ts +1 -0
- package/project/context/flowr-analyzer-files-context.js +4 -0
- package/project/context/flowr-analyzer-loading-order-context.d.ts +1 -0
- package/project/context/flowr-analyzer-loading-order-context.js +6 -0
- package/project/flowr-analyzer.d.ts +19 -18
- package/project/flowr-analyzer.js +15 -8
- package/queries/catalog/call-context-query/call-context-query-executor.js +2 -1
- package/queries/catalog/config-query/config-query-format.d.ts +1 -1
- package/queries/catalog/control-flow-query/control-flow-query-executor.js +2 -1
- package/queries/catalog/df-shape-query/df-shape-query-executor.js +1 -1
- package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.d.ts +3 -0
- package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.js +45 -0
- package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.d.ts +22 -0
- package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.js +32 -0
- package/queries/query.d.ts +10 -2
- package/queries/query.js +2 -0
- package/r-bridge/parser.d.ts +7 -0
- package/search/search-executor/search-enrichers.js +2 -1
- package/util/r-value.d.ts +1 -1
- package/util/r-value.js +2 -0
- package/util/version.d.ts +17 -0
- package/util/version.js +28 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isHigherOrder = isHigherOrder;
|
|
4
|
+
const vertex_1 = require("../graph/vertex");
|
|
5
|
+
const assert_1 = require("../../util/assert");
|
|
6
|
+
const edge_1 = require("../graph/edge");
|
|
7
|
+
const alias_tracking_1 = require("../eval/resolve/alias-tracking");
|
|
8
|
+
const config_1 = require("../../config");
|
|
9
|
+
const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
10
|
+
const general_1 = require("../eval/values/general");
|
|
11
|
+
function isAnyReturnAFunction(def, graph) {
|
|
12
|
+
const workingQueue = def.exitPoints.map(d => graph.getVertex(d, true)).filter(assert_1.isNotUndefined);
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
while (workingQueue.length > 0) {
|
|
15
|
+
const current = workingQueue.pop();
|
|
16
|
+
if (seen.has(current.id)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
seen.add(current.id);
|
|
20
|
+
if ((0, vertex_1.isFunctionDefinitionVertex)(current)) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
const next = graph.outgoingEdges(current.id) ?? [];
|
|
24
|
+
for (const [t, { types }] of next) {
|
|
25
|
+
if ((0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.Returns)) {
|
|
26
|
+
const v = graph.getVertex(t, true);
|
|
27
|
+
if (v) {
|
|
28
|
+
workingQueue.push(v);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
function inspectCallSitesArgumentsFns(def, graph) {
|
|
36
|
+
const callSites = graph.ingoingEdges(def.id);
|
|
37
|
+
for (const [callerId, { types }] of callSites ?? []) {
|
|
38
|
+
if (!(0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.Calls)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const caller = graph.getVertex(callerId, true);
|
|
42
|
+
if (!caller || !(0, vertex_1.isFunctionCallVertex)(caller)) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
for (const arg of caller.args) {
|
|
46
|
+
if (arg === r_function_call_1.EmptyArgument) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const value = (0, general_1.valueSetGuard)((0, alias_tracking_1.resolveIdToValue)(arg.nodeId, { graph, idMap: graph.idMap, resolve: config_1.VariableResolve.Alias, full: true }));
|
|
50
|
+
if (value?.elements.some(e => e.type === 'function-definition')) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Determines whether the function with the given id is a higher-order function, i.e.,
|
|
59
|
+
* either takes a function as an argument or (may) returns a function.
|
|
60
|
+
* If the return is an identity, e.g., `function(x) x`, this is not considered higher-order,
|
|
61
|
+
* if no function is passed as an argument.
|
|
62
|
+
*/
|
|
63
|
+
function isHigherOrder(id, graph) {
|
|
64
|
+
const vert = graph.getVertex(id);
|
|
65
|
+
if (!vert || !(0, vertex_1.isFunctionDefinitionVertex)(vert)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
// 1. check whether any of the exit types is a function
|
|
69
|
+
if (isAnyReturnAFunction(vert, graph)) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
// 2. check whether any of the callsites passes a function
|
|
73
|
+
return inspectCallSitesArgumentsFns(vert, graph);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=higher-order-function.js.map
|
|
@@ -11,7 +11,7 @@ const doc_docker_1 = require("./doc-docker");
|
|
|
11
11
|
const prompt_1 = require("../../cli/repl/prompt");
|
|
12
12
|
const doc_code_1 = require("./doc-code");
|
|
13
13
|
const print_version_1 = require("../../cli/repl/print-version");
|
|
14
|
-
const
|
|
14
|
+
const flowr_analyzer_builder_1 = require("../../project/flowr-analyzer-builder");
|
|
15
15
|
function printHelpForScript(script, starredVersion) {
|
|
16
16
|
let base = `| **${(0, doc_cli_option_1.getReplCommand)(script[0], false, starredVersion !== undefined)}** | ${script[1].description}`;
|
|
17
17
|
if (starredVersion) {
|
|
@@ -52,7 +52,10 @@ async function documentReplSession(parser, commands, options) {
|
|
|
52
52
|
entry.lines.push(msg);
|
|
53
53
|
}
|
|
54
54
|
};
|
|
55
|
-
await
|
|
55
|
+
const analyzer = await new flowr_analyzer_builder_1.FlowrAnalyzerBuilder()
|
|
56
|
+
.setParser(parser)
|
|
57
|
+
.build();
|
|
58
|
+
await (0, core_1.replProcessAnswer)(analyzer, collectingOutput, command.command, options?.allowRSessionAccess ?? false);
|
|
56
59
|
collect.push(entry);
|
|
57
60
|
}
|
|
58
61
|
let result = '';
|
|
@@ -921,8 +921,8 @@ ${(0, doc_types_1.printHierarchy)({ program: vertexType.program, info: vertexTyp
|
|
|
921
921
|
Let's start by looking at the properties of the dataflow information object: ${Object.keys(result).map(k => `\`${k}\``).join(', ')}.
|
|
922
922
|
|
|
923
923
|
${(() => {
|
|
924
|
-
/* this includes the meta field for timing */
|
|
925
|
-
(0, assert_1.guard)(Object.keys(result).length ===
|
|
924
|
+
/* this includes the meta field for timing and the quick CFG in order to enable re-use and improve performance */
|
|
925
|
+
(0, assert_1.guard)(Object.keys(result).length === 9, () => 'Update Dataflow Documentation!');
|
|
926
926
|
return '';
|
|
927
927
|
})()}
|
|
928
928
|
|
|
@@ -39,6 +39,7 @@ const doc_cfg_1 = require("./doc-util/doc-cfg");
|
|
|
39
39
|
const df_shape_query_executor_1 = require("../queries/catalog/df-shape-query/df-shape-query-executor");
|
|
40
40
|
const _00_slice_1 = require("../core/steps/all/static-slicing/00-slice");
|
|
41
41
|
const doc_repl_1 = require("./doc-util/doc-repl");
|
|
42
|
+
const inspect_higher_order_query_executor_1 = require("../queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor");
|
|
42
43
|
(0, doc_query_1.registerQueryDocumentation)('call-context', {
|
|
43
44
|
name: 'Call-Context Query',
|
|
44
45
|
type: 'active',
|
|
@@ -243,6 +244,25 @@ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
|
|
|
243
244
|
`;
|
|
244
245
|
}
|
|
245
246
|
});
|
|
247
|
+
(0, doc_query_1.registerQueryDocumentation)('inspect-higher-order', {
|
|
248
|
+
name: 'Inspect Higher-Order Functions Query',
|
|
249
|
+
type: 'active',
|
|
250
|
+
shortDescription: 'Determine whether functions are higher-order functions',
|
|
251
|
+
functionName: inspect_higher_order_query_executor_1.executeHigherOrderQuery.name,
|
|
252
|
+
functionFile: '../queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.ts',
|
|
253
|
+
buildExplanation: async (shell) => {
|
|
254
|
+
const exampleCode = 'f <- function() function(x) x; f()';
|
|
255
|
+
return `
|
|
256
|
+
With this query you can identify which functions in the code are higher-order functions, i.e., either take a function as an argument or return a function.
|
|
257
|
+
Please note, that functions that are just identities (e.g., \`function(x) x\`) are not considered higher-order if they do not take a function as an argument.
|
|
258
|
+
|
|
259
|
+
Using the example code \`${exampleCode}\` the following query returns the information for all identified function definitions whether they are higher-order functions:
|
|
260
|
+
${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
|
|
261
|
+
type: 'inspect-higher-order',
|
|
262
|
+
}], { showCode: true })}
|
|
263
|
+
`;
|
|
264
|
+
}
|
|
265
|
+
});
|
|
246
266
|
(0, doc_query_1.registerQueryDocumentation)('origin', {
|
|
247
267
|
name: 'Origin Query',
|
|
248
268
|
type: 'active',
|
|
@@ -167,7 +167,7 @@ We welcome every contribution! Please check out the [developer onboarding](${doc
|
|
|
167
167
|
|
|
168
168
|
----
|
|
169
169
|
|
|
170
|
-
*flowr* is actively developed by [Florian Sihler](https://eagleoutice.github.io/portfolio/) under the
|
|
170
|
+
*flowr* is actively developed by [Florian Sihler](https://eagleoutice.github.io/portfolio/) and (since October 1st 2025) [Oliver Gerstl](https://www.linkedin.com/in/oliver-gerstl) under the
|
|
171
171
|
[GPLv3 License](LICENSE).\\
|
|
172
172
|
It is partially supported by the German Research Foundation (DFG) under the grant [504226141](https://gepris.dfg.de/gepris/projekt/504226141) ("CodeInspector").
|
|
173
173
|
|
package/package.json
CHANGED
|
@@ -10,6 +10,7 @@ import type { TreeSitterExecutor } from '../../r-bridge/lang-4.x/tree-sitter/tre
|
|
|
10
10
|
import type { PipelineOutput } from '../../core/steps/pipeline/pipeline';
|
|
11
11
|
import type { CfgSimplificationPassName } from '../../control-flow/cfg-simplification';
|
|
12
12
|
import type { ControlFlowInformation } from '../../control-flow/control-flow-graph';
|
|
13
|
+
import { CfgKind } from '../cfg-kind';
|
|
13
14
|
interface FlowrAnalyzerCacheOptions<Parser extends KnownParser> {
|
|
14
15
|
parser: Parser;
|
|
15
16
|
config: FlowrConfigOptions;
|
|
@@ -17,7 +18,7 @@ interface FlowrAnalyzerCacheOptions<Parser extends KnownParser> {
|
|
|
17
18
|
getId?: IdGenerator<NoInfo>;
|
|
18
19
|
overwriteFilePath?: string;
|
|
19
20
|
}
|
|
20
|
-
type AnalyzerCacheType<Parser extends KnownParser> = Parser extends TreeSitterExecutor ? Partial<PipelineOutput<typeof TREE_SITTER_DATAFLOW_PIPELINE>> : Partial<PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>>;
|
|
21
|
+
export type AnalyzerCacheType<Parser extends KnownParser> = Parser extends TreeSitterExecutor ? Partial<PipelineOutput<typeof TREE_SITTER_DATAFLOW_PIPELINE>> : Partial<PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>>;
|
|
21
22
|
/**
|
|
22
23
|
* This provides the full analyzer caching layer, please avoid using this directly
|
|
23
24
|
* and prefer the {@link FlowrAnalyzer}.
|
|
@@ -77,17 +78,17 @@ export declare class FlowrAnalyzerCache<Parser extends KnownParser> extends Flow
|
|
|
77
78
|
/**
|
|
78
79
|
* Get the control flow graph (CFG) for the request, computing if necessary.
|
|
79
80
|
* @param force - Do not use the cache, instead force new analyses.
|
|
80
|
-
* @param
|
|
81
|
+
* @param kind - The kind of CFG that is requested.
|
|
81
82
|
* @param simplifications - Simplification passes to be applied to the CFG.
|
|
82
83
|
*/
|
|
83
|
-
controlflow(force: boolean | undefined,
|
|
84
|
+
controlflow(force: boolean | undefined, kind: CfgKind, simplifications: readonly CfgSimplificationPassName[] | undefined): Promise<ControlFlowInformation>;
|
|
84
85
|
/**
|
|
85
86
|
* Get the control flow graph (CFG) for the request if already available, otherwise return `undefined`.
|
|
86
|
-
* @param
|
|
87
|
+
* @param kind - The kind of CFG that is requested.
|
|
87
88
|
* @param simplifications - Simplification passes to be applied to the CFG.
|
|
88
89
|
*
|
|
89
90
|
* @see {@link FlowrAnalyzerCache#controlflow} - to get the control flow graph, computing if necessary.
|
|
90
91
|
*/
|
|
91
|
-
peekControlflow(
|
|
92
|
+
peekControlflow(kind: CfgKind, simplifications: readonly CfgSimplificationPassName[] | undefined): ControlFlowInformation | undefined;
|
|
92
93
|
}
|
|
93
94
|
export {};
|
|
@@ -6,6 +6,7 @@ const default_pipelines_1 = require("../../core/steps/pipeline/default-pipelines
|
|
|
6
6
|
const assert_1 = require("../../util/assert");
|
|
7
7
|
const objectmap_1 = require("../../util/collections/objectmap");
|
|
8
8
|
const extract_cfg_1 = require("../../control-flow/extract-cfg");
|
|
9
|
+
const cfg_kind_1 = require("../cfg-kind");
|
|
9
10
|
/**
|
|
10
11
|
* This provides the full analyzer caching layer, please avoid using this directly
|
|
11
12
|
* and prefer the {@link FlowrAnalyzer}.
|
|
@@ -50,6 +51,7 @@ class FlowrAnalyzerCache extends flowr_cache_1.FlowrCache {
|
|
|
50
51
|
this.receive({ type: "full" /* CacheInvalidationEventType.Full */ });
|
|
51
52
|
}
|
|
52
53
|
async runTapeUntil(force, until) {
|
|
54
|
+
(0, assert_1.guard)(this.args.request && (Array.isArray(this.args.request) ? this.args.request.length > 0 : true), 'At least one request must be set to run the analysis pipeline');
|
|
53
55
|
if (force) {
|
|
54
56
|
this.reset();
|
|
55
57
|
}
|
|
@@ -119,37 +121,43 @@ class FlowrAnalyzerCache extends flowr_cache_1.FlowrCache {
|
|
|
119
121
|
/**
|
|
120
122
|
* Get the control flow graph (CFG) for the request, computing if necessary.
|
|
121
123
|
* @param force - Do not use the cache, instead force new analyses.
|
|
122
|
-
* @param
|
|
124
|
+
* @param kind - The kind of CFG that is requested.
|
|
123
125
|
* @param simplifications - Simplification passes to be applied to the CFG.
|
|
124
126
|
*/
|
|
125
|
-
async controlflow(force,
|
|
127
|
+
async controlflow(force, kind, simplifications) {
|
|
128
|
+
(0, assert_1.guard)(kind === cfg_kind_1.CfgKind.Quick ? simplifications === undefined : true, 'Cannot apply simplifications to quick CFG');
|
|
126
129
|
simplifications ??= [];
|
|
127
130
|
if (!force) {
|
|
128
|
-
const value = this.controlFlowCache.simplified.get([simplifications,
|
|
131
|
+
const value = this.controlFlowCache.simplified.get([simplifications, kind]);
|
|
129
132
|
if (value !== undefined) {
|
|
130
133
|
return value;
|
|
131
134
|
}
|
|
132
135
|
}
|
|
133
136
|
const normalized = await this.normalize(force);
|
|
134
|
-
let
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
let result;
|
|
138
|
+
switch (kind) {
|
|
139
|
+
case cfg_kind_1.CfgKind.WithDataflow:
|
|
140
|
+
result = (0, extract_cfg_1.extractCfg)(normalized, this.args.config, (await this.dataflow()).graph, simplifications);
|
|
141
|
+
break;
|
|
142
|
+
case cfg_kind_1.CfgKind.NoDataflow:
|
|
143
|
+
result = (0, extract_cfg_1.extractCfg)(normalized, this.args.config, undefined, simplifications);
|
|
144
|
+
break;
|
|
145
|
+
case cfg_kind_1.CfgKind.Quick:
|
|
146
|
+
result = this.peekDataflow()?.cfgQuick ?? (0, extract_cfg_1.extractCfgQuick)(normalized);
|
|
147
|
+
break;
|
|
138
148
|
}
|
|
139
|
-
|
|
140
|
-
(0, extract_cfg_1.extractCfg)(normalized, this.args.config, dataflow?.graph, simplifications);
|
|
141
|
-
this.controlFlowCache.simplified.set([simplifications, useDataflow], result);
|
|
149
|
+
this.controlFlowCache.simplified.set([simplifications, kind], result);
|
|
142
150
|
return result;
|
|
143
151
|
}
|
|
144
152
|
/**
|
|
145
153
|
* Get the control flow graph (CFG) for the request if already available, otherwise return `undefined`.
|
|
146
|
-
* @param
|
|
154
|
+
* @param kind - The kind of CFG that is requested.
|
|
147
155
|
* @param simplifications - Simplification passes to be applied to the CFG.
|
|
148
156
|
*
|
|
149
157
|
* @see {@link FlowrAnalyzerCache#controlflow} - to get the control flow graph, computing if necessary.
|
|
150
158
|
*/
|
|
151
|
-
peekControlflow(
|
|
152
|
-
return this.controlFlowCache.simplified.get([simplifications ?? [],
|
|
159
|
+
peekControlflow(kind, simplifications) {
|
|
160
|
+
return this.controlFlowCache.simplified.get([simplifications ?? [], kind]);
|
|
153
161
|
}
|
|
154
162
|
}
|
|
155
163
|
exports.FlowrAnalyzerCache = FlowrAnalyzerCache;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Denotes the kind of control flow graph (CFG).
|
|
3
|
+
*/
|
|
4
|
+
export declare enum CfgKind {
|
|
5
|
+
/**
|
|
6
|
+
* CFG with dataflow information.
|
|
7
|
+
*/
|
|
8
|
+
WithDataflow = 0,
|
|
9
|
+
/**
|
|
10
|
+
* CFG without dataflow information.
|
|
11
|
+
*/
|
|
12
|
+
NoDataflow = 1,
|
|
13
|
+
/**
|
|
14
|
+
* A CFG version that is much quicker and does not apply any simplifications or dataflow information.
|
|
15
|
+
*/
|
|
16
|
+
Quick = 2
|
|
17
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CfgKind = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Denotes the kind of control flow graph (CFG).
|
|
6
|
+
*/
|
|
7
|
+
var CfgKind;
|
|
8
|
+
(function (CfgKind) {
|
|
9
|
+
/**
|
|
10
|
+
* CFG with dataflow information.
|
|
11
|
+
*/
|
|
12
|
+
CfgKind[CfgKind["WithDataflow"] = 0] = "WithDataflow";
|
|
13
|
+
/**
|
|
14
|
+
* CFG without dataflow information.
|
|
15
|
+
*/
|
|
16
|
+
CfgKind[CfgKind["NoDataflow"] = 1] = "NoDataflow";
|
|
17
|
+
/**
|
|
18
|
+
* A CFG version that is much quicker and does not apply any simplifications or dataflow information.
|
|
19
|
+
*/
|
|
20
|
+
CfgKind[CfgKind["Quick"] = 2] = "Quick";
|
|
21
|
+
})(CfgKind || (exports.CfgKind = CfgKind = {}));
|
|
22
|
+
//# sourceMappingURL=cfg-kind.js.map
|
|
@@ -32,4 +32,8 @@ export declare abstract class AbstractFlowrAnalyzerContext<In, Out, Plugin exten
|
|
|
32
32
|
* Returns the project context this sub-context is attached to
|
|
33
33
|
*/
|
|
34
34
|
getAttachedContext(): FlowrAnalyzerContext;
|
|
35
|
+
/**
|
|
36
|
+
* Reset the context to its initial state.
|
|
37
|
+
*/
|
|
38
|
+
abstract reset(): void;
|
|
35
39
|
}
|
|
@@ -37,6 +37,8 @@ export declare class FlowrAnalyzerContext implements ReadOnlyFlowrAnalyzerContex
|
|
|
37
37
|
constructor(plugins: ReadonlyMap<PluginType, readonly FlowrAnalyzerPlugin[]>);
|
|
38
38
|
/** delegate request addition */
|
|
39
39
|
addRequests(requests: readonly RAnalysisRequest[]): void;
|
|
40
|
+
/** delegate request addition */
|
|
41
|
+
addRequest(request: RAnalysisRequest): void;
|
|
40
42
|
/** this conducts all the step that can be done before the main analysis run */
|
|
41
43
|
resolvePreAnalysis(): void;
|
|
42
44
|
/**
|
|
@@ -45,4 +47,8 @@ export declare class FlowrAnalyzerContext implements ReadOnlyFlowrAnalyzerContex
|
|
|
45
47
|
* the available methods.
|
|
46
48
|
*/
|
|
47
49
|
inspect(): ReadOnlyFlowrAnalyzerContext;
|
|
50
|
+
/**
|
|
51
|
+
* Reset the context to its initial state, removing all files, dependencies, and loading orders.
|
|
52
|
+
*/
|
|
53
|
+
reset(): void;
|
|
48
54
|
}
|
|
@@ -29,6 +29,10 @@ class FlowrAnalyzerContext {
|
|
|
29
29
|
addRequests(requests) {
|
|
30
30
|
this.files.addRequests(requests);
|
|
31
31
|
}
|
|
32
|
+
/** delegate request addition */
|
|
33
|
+
addRequest(request) {
|
|
34
|
+
this.files.addRequest(request);
|
|
35
|
+
}
|
|
32
36
|
/** this conducts all the step that can be done before the main analysis run */
|
|
33
37
|
resolvePreAnalysis() {
|
|
34
38
|
this.files.computeLoadingOrder();
|
|
@@ -42,6 +46,13 @@ class FlowrAnalyzerContext {
|
|
|
42
46
|
inspect() {
|
|
43
47
|
return this;
|
|
44
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Reset the context to its initial state, removing all files, dependencies, and loading orders.
|
|
51
|
+
*/
|
|
52
|
+
reset() {
|
|
53
|
+
this.files.reset();
|
|
54
|
+
this.deps.reset();
|
|
55
|
+
}
|
|
45
56
|
}
|
|
46
57
|
exports.FlowrAnalyzerContext = FlowrAnalyzerContext;
|
|
47
58
|
//# sourceMappingURL=flowr-analyzer-context.js.map
|
|
@@ -31,6 +31,7 @@ export declare class FlowrAnalyzerDependenciesContext extends AbstractFlowrAnaly
|
|
|
31
31
|
readonly name = "flowr-analyzer-dependencies-context";
|
|
32
32
|
private dependencies;
|
|
33
33
|
private staticsLoaded;
|
|
34
|
+
reset(): void;
|
|
34
35
|
constructor(ctx: FlowrAnalyzerContext, plugins?: readonly FlowrAnalyzerPackageVersionsPlugin[]);
|
|
35
36
|
resolveStaticDependencies(): void;
|
|
36
37
|
addDependency(pkg: Package): void;
|
|
@@ -12,6 +12,10 @@ class FlowrAnalyzerDependenciesContext extends abstract_flowr_analyzer_context_1
|
|
|
12
12
|
name = 'flowr-analyzer-dependencies-context';
|
|
13
13
|
dependencies = new Map();
|
|
14
14
|
staticsLoaded = false;
|
|
15
|
+
reset() {
|
|
16
|
+
this.dependencies = new Map();
|
|
17
|
+
this.staticsLoaded = false;
|
|
18
|
+
}
|
|
15
19
|
constructor(ctx, plugins) {
|
|
16
20
|
super(ctx, flowr_analyzer_package_versions_plugin_1.FlowrAnalyzerPackageVersionsPlugin.defaultPlugin(), plugins);
|
|
17
21
|
}
|
|
@@ -59,6 +59,7 @@ export declare class FlowrAnalyzerFilesContext extends AbstractFlowrAnalyzerCont
|
|
|
59
59
|
private readonly fileLoaders;
|
|
60
60
|
private specialFiles;
|
|
61
61
|
constructor(loadingOrder: FlowrAnalyzerLoadingOrderContext, plugins: readonly FlowrAnalyzerProjectDiscoveryPlugin[], fileLoaders: readonly FlowrAnalyzerFilePlugin[]);
|
|
62
|
+
reset(): void;
|
|
62
63
|
/**
|
|
63
64
|
* Add multiple requests to the context. This is just a convenience method that calls {@link addRequest} for each request.
|
|
64
65
|
*/
|
|
@@ -49,6 +49,10 @@ class FlowrAnalyzerFilesContext extends abstract_flowr_analyzer_context_1.Abstra
|
|
|
49
49
|
this.fileLoaders = [...fileLoaders, flowr_analyzer_file_plugin_1.FlowrAnalyzerFilePlugin.defaultPlugin()];
|
|
50
50
|
this.loadingOrder = loadingOrder;
|
|
51
51
|
}
|
|
52
|
+
reset() {
|
|
53
|
+
this.loadingOrder.reset();
|
|
54
|
+
this.files = new Map();
|
|
55
|
+
}
|
|
52
56
|
/**
|
|
53
57
|
* Add multiple requests to the context. This is just a convenience method that calls {@link addRequest} for each request.
|
|
54
58
|
*/
|
|
@@ -49,6 +49,7 @@ export declare class FlowrAnalyzerLoadingOrderContext extends AbstractFlowrAnaly
|
|
|
49
49
|
private guesses;
|
|
50
50
|
/** just the base collection of requests we know nothing about the order! */
|
|
51
51
|
private unordered;
|
|
52
|
+
reset(): void;
|
|
52
53
|
/**
|
|
53
54
|
* Add one or multiple requests to the context.
|
|
54
55
|
* These are considered unordered (i.e., ordered implicitly by the order of addition) until a plugin provides either a guess or a known order.
|
|
@@ -23,6 +23,12 @@ class FlowrAnalyzerLoadingOrderContext extends abstract_flowr_analyzer_context_1
|
|
|
23
23
|
guesses = [];
|
|
24
24
|
/** just the base collection of requests we know nothing about the order! */
|
|
25
25
|
unordered = [];
|
|
26
|
+
reset() {
|
|
27
|
+
this.knownOrder = undefined;
|
|
28
|
+
this.guesses.length = 0;
|
|
29
|
+
this.unordered.length = 0;
|
|
30
|
+
this.rerunRequired = this.plugins.length > 0;
|
|
31
|
+
}
|
|
26
32
|
/**
|
|
27
33
|
* Add one or multiple requests to the context.
|
|
28
34
|
* These are considered unordered (i.e., ordered implicitly by the order of addition) until a plugin provides either a guess or a known order.
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type { FlowrConfigOptions } from '../config';
|
|
2
|
-
import type { KnownParser,
|
|
2
|
+
import type { KnownParser, ParseStepOutput, RShellInformation, TreeSitterInformation } from '../r-bridge/parser';
|
|
3
3
|
import type { Queries, QueryResults, SupportedQueryTypes } from '../queries/query';
|
|
4
4
|
import type { ControlFlowInformation } from '../control-flow/control-flow-graph';
|
|
5
5
|
import type { NormalizedAst } from '../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
6
6
|
import type { DataflowInformation } from '../dataflow/info';
|
|
7
7
|
import type { CfgSimplificationPassName } from '../control-flow/cfg-simplification';
|
|
8
8
|
import type { PipelinePerStepMetaInformation } from '../core/steps/pipeline/pipeline';
|
|
9
|
-
import type { FlowrAnalyzerCache } from './cache/flowr-analyzer-cache';
|
|
9
|
+
import type { AnalyzerCacheType, FlowrAnalyzerCache } from './cache/flowr-analyzer-cache';
|
|
10
10
|
import type { FlowrSearchLike, SearchOutput } from '../search/flowr-search-builder';
|
|
11
11
|
import type { GetSearchElements } from '../search/flowr-search-executor';
|
|
12
12
|
import type { FlowrAnalyzerContext, ReadOnlyFlowrAnalyzerContext } from './context/flowr-analyzer-context';
|
|
13
|
+
import { CfgKind } from './cfg-kind';
|
|
14
|
+
import type { OutputCollectorConfiguration } from '../r-bridge/shell';
|
|
13
15
|
/**
|
|
14
16
|
* Exposes the central analyses and information provided by the {@link FlowrAnalyzer} to the linter, search, and query APIs.
|
|
15
17
|
* This allows us to exchange the underlying implementation of the analyzer without affecting the APIs.
|
|
@@ -18,7 +20,13 @@ export interface FlowrAnalysisProvider {
|
|
|
18
20
|
/**
|
|
19
21
|
* Get the name of the parser used by the analyzer.
|
|
20
22
|
*/
|
|
21
|
-
|
|
23
|
+
parserInformation(): Promise<TreeSitterInformation | RShellInformation>;
|
|
24
|
+
/**
|
|
25
|
+
* Sends a command to the underlying R engine and collects the output.
|
|
26
|
+
* @param command - The command to send to the R engine.
|
|
27
|
+
* @param addonConfig - Additional configuration for the output collector.
|
|
28
|
+
*/
|
|
29
|
+
sendCommandWithOutput(command: string, addonConfig?: Partial<OutputCollectorConfiguration>): Promise<string[]>;
|
|
22
30
|
/**
|
|
23
31
|
* Returns project context information.
|
|
24
32
|
* If you are a user that wants to inspect the context, prefer {@link inspectContext} instead.
|
|
@@ -50,17 +58,10 @@ export interface FlowrAnalysisProvider {
|
|
|
50
58
|
/**
|
|
51
59
|
* Get the control flow graph (CFG) for the request.
|
|
52
60
|
* @param simplifications - Simplification passes to be applied to the CFG.
|
|
53
|
-
* @param
|
|
61
|
+
* @param kind - The kind of CFG that is requested. By default, the CFG without dataflow information is returned.
|
|
54
62
|
* @param force - Do not use the cache, instead force new analyses.
|
|
55
63
|
*/
|
|
56
|
-
controlflow(simplifications?: readonly CfgSimplificationPassName[],
|
|
57
|
-
/**
|
|
58
|
-
* Get a quick and dirty control flow graph (CFG) for the request.
|
|
59
|
-
* This does not use the dataflow information and does not apply any simplifications.
|
|
60
|
-
*
|
|
61
|
-
* @param force - Do not use the cache, instead force new analyses.
|
|
62
|
-
*/
|
|
63
|
-
controlflowQuick(force?: boolean): Promise<ControlFlowInformation>;
|
|
64
|
+
controlflow(simplifications?: readonly CfgSimplificationPassName[], kind?: CfgKind, force?: boolean): Promise<ControlFlowInformation>;
|
|
64
65
|
/**
|
|
65
66
|
* Access the query API for the request.
|
|
66
67
|
* @param query - The list of queries.
|
|
@@ -107,15 +108,15 @@ export declare class FlowrAnalyzer<Parser extends KnownParser = KnownParser> imp
|
|
|
107
108
|
*/
|
|
108
109
|
constructor(config: FlowrConfigOptions, parser: Parser, ctx: FlowrAnalyzerContext, cache: FlowrAnalyzerCache<Parser>);
|
|
109
110
|
context(): FlowrAnalyzerContext;
|
|
111
|
+
parserInformation(): Promise<TreeSitterInformation | RShellInformation>;
|
|
112
|
+
sendCommandWithOutput(command: string, addonConfig?: Partial<OutputCollectorConfiguration>): Promise<string[]>;
|
|
110
113
|
inspectContext(): ReadOnlyFlowrAnalyzerContext;
|
|
111
114
|
reset(): void;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
dataflow(force?: boolean): ReturnType<typeof this.cache.dataflow>;
|
|
115
|
+
parse(force?: boolean): Promise<NonNullable<AnalyzerCacheType<Parser>['parse']>>;
|
|
116
|
+
normalize(force?: boolean): Promise<NonNullable<AnalyzerCacheType<Parser>['normalize']>>;
|
|
117
|
+
dataflow(force?: boolean): Promise<NonNullable<AnalyzerCacheType<Parser>['dataflow']>>;
|
|
116
118
|
runFull(force?: boolean): Promise<void>;
|
|
117
|
-
controlflow(simplifications?: readonly CfgSimplificationPassName[],
|
|
118
|
-
controlflowQuick(force?: boolean): Promise<ControlFlowInformation>;
|
|
119
|
+
controlflow(simplifications?: readonly CfgSimplificationPassName[], kind?: CfgKind, force?: boolean): Promise<ControlFlowInformation>;
|
|
119
120
|
query<Types extends SupportedQueryTypes = SupportedQueryTypes>(query: Queries<Types>): Promise<QueryResults<Types>>;
|
|
120
121
|
runSearch<Search extends FlowrSearchLike>(search: Search): Promise<GetSearchElements<SearchOutput<Search>>>;
|
|
121
122
|
/**
|
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FlowrAnalyzer = void 0;
|
|
4
4
|
const query_1 = require("../queries/query");
|
|
5
5
|
const flowr_search_executor_1 = require("../search/flowr-search-executor");
|
|
6
|
+
const cfg_kind_1 = require("./cfg-kind");
|
|
7
|
+
const shell_1 = require("../r-bridge/shell");
|
|
8
|
+
const assert_1 = require("../util/assert");
|
|
6
9
|
/**
|
|
7
10
|
* Central class for conducting analyses with FlowR.
|
|
8
11
|
* Use the {@link FlowrAnalyzerBuilder} to create a new instance.
|
|
@@ -36,15 +39,22 @@ class FlowrAnalyzer {
|
|
|
36
39
|
context() {
|
|
37
40
|
return this.ctx;
|
|
38
41
|
}
|
|
42
|
+
async parserInformation() {
|
|
43
|
+
return this.parser.name === 'r-shell' ?
|
|
44
|
+
{ name: 'r-shell', rVersion: await this.parser.rVersion() }
|
|
45
|
+
: { name: 'tree-sitter' };
|
|
46
|
+
}
|
|
47
|
+
async sendCommandWithOutput(command, addonConfig) {
|
|
48
|
+
(0, assert_1.guard)(this.parser instanceof shell_1.RShell, 'sendCommandWithOutput can only be used with RShell parsers!');
|
|
49
|
+
return this.parser.sendCommandWithOutput(command, addonConfig);
|
|
50
|
+
}
|
|
39
51
|
inspectContext() {
|
|
40
52
|
return this.ctx.inspect();
|
|
41
53
|
}
|
|
42
54
|
reset() {
|
|
55
|
+
this.ctx.reset();
|
|
43
56
|
this.cache.reset();
|
|
44
57
|
}
|
|
45
|
-
parserName() {
|
|
46
|
-
return this.parser.name;
|
|
47
|
-
}
|
|
48
58
|
async parse(force) {
|
|
49
59
|
return this.cache.parse(force);
|
|
50
60
|
}
|
|
@@ -58,11 +68,8 @@ class FlowrAnalyzer {
|
|
|
58
68
|
await this.dataflow(force);
|
|
59
69
|
return;
|
|
60
70
|
}
|
|
61
|
-
async controlflow(simplifications,
|
|
62
|
-
return this.cache.controlflow(force,
|
|
63
|
-
}
|
|
64
|
-
async controlflowQuick(force) {
|
|
65
|
-
return this.controlflow(undefined, false, force);
|
|
71
|
+
async controlflow(simplifications, kind, force) {
|
|
72
|
+
return this.cache.controlflow(force, kind ?? cfg_kind_1.CfgKind.NoDataflow, simplifications);
|
|
66
73
|
}
|
|
67
74
|
async query(query) {
|
|
68
75
|
return (0, query_1.executeQueries)({ analyzer: this }, query);
|
|
@@ -8,6 +8,7 @@ const edge_1 = require("../../../dataflow/graph/edge");
|
|
|
8
8
|
const two_layer_collector_1 = require("../../two-layer-collector");
|
|
9
9
|
const objects_1 = require("../../../util/objects");
|
|
10
10
|
const identify_link_to_last_call_relation_1 = require("./identify-link-to-last-call-relation");
|
|
11
|
+
const cfg_kind_1 = require("../../../project/cfg-kind");
|
|
11
12
|
/* if the node is effected by nse, we have an ingoing nse edge */
|
|
12
13
|
function isQuoted(node, graph) {
|
|
13
14
|
const vertex = graph.ingoingEdges(node);
|
|
@@ -184,7 +185,7 @@ async function executeCallContextQueries({ analyzer }, queries) {
|
|
|
184
185
|
const { promotedQueries, requiresCfg } = promoteQueryCallNames(queries);
|
|
185
186
|
let cfg = undefined;
|
|
186
187
|
if (requiresCfg) {
|
|
187
|
-
cfg = await analyzer.controlflow([],
|
|
188
|
+
cfg = await analyzer.controlflow([], cfg_kind_1.CfgKind.WithDataflow);
|
|
188
189
|
}
|
|
189
190
|
const queriesWhichWantAliases = promotedQueries.filter(q => q.includeAliases);
|
|
190
191
|
for (const [nodeId, info] of dataflow.graph.vertices(true)) {
|
|
@@ -15,7 +15,7 @@ declare function configReplCompleter(partialLine: readonly string[], config: Flo
|
|
|
15
15
|
declare function configQueryLineParser(line: readonly string[], _config: FlowrConfigOptions): [ConfigQuery];
|
|
16
16
|
export declare const ConfigQueryDefinition: {
|
|
17
17
|
readonly executor: typeof executeConfigQuery;
|
|
18
|
-
readonly asciiSummarizer: (formatter: OutputFormatter, _analyzer: unknown, queryResults: BaseQueryResult, result: string[]) =>
|
|
18
|
+
readonly asciiSummarizer: (formatter: OutputFormatter, _analyzer: unknown, queryResults: BaseQueryResult, result: string[]) => true;
|
|
19
19
|
readonly completer: typeof configReplCompleter;
|
|
20
20
|
readonly fromLine: typeof configQueryLineParser;
|
|
21
21
|
readonly schema: Joi.ObjectSchema<any>;
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.executeControlFlowQuery = executeControlFlowQuery;
|
|
4
4
|
const log_1 = require("../../../util/log");
|
|
5
|
+
const cfg_kind_1 = require("../../../project/cfg-kind");
|
|
5
6
|
async function executeControlFlowQuery({ analyzer }, queries) {
|
|
6
7
|
if (queries.length !== 1) {
|
|
7
8
|
log_1.log.warn('The control flow query expects only up to one query, but got', queries.length);
|
|
8
9
|
}
|
|
9
10
|
const query = queries[0];
|
|
10
11
|
const start = Date.now();
|
|
11
|
-
const controlFlow = await analyzer.controlflow(query.config?.simplificationPasses,
|
|
12
|
+
const controlFlow = await analyzer.controlflow(query.config?.simplificationPasses, cfg_kind_1.CfgKind.WithDataflow);
|
|
12
13
|
return {
|
|
13
14
|
'.meta': {
|
|
14
15
|
timing: Date.now() - start
|
|
@@ -9,8 +9,8 @@ async function executeDfShapeQuery({ analyzer }, queries) {
|
|
|
9
9
|
log_1.log.warn('The dataframe shape query expects only up to one query without slicing criterion, but got', queries.length);
|
|
10
10
|
queries = [{ type: 'df-shape' }];
|
|
11
11
|
}
|
|
12
|
-
const ast = await analyzer.normalize();
|
|
13
12
|
const graph = (await analyzer.dataflow()).graph;
|
|
13
|
+
const ast = await analyzer.normalize();
|
|
14
14
|
const start = Date.now();
|
|
15
15
|
const domains = (0, shape_inference_1.inferDataFrameShapes)(await analyzer.controlflow(), graph, ast, analyzer.flowrConfig);
|
|
16
16
|
if (queries.length === 1 && queries[0].criterion === undefined) {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { InspectHigherOrderQuery, InspectHigherOrderQueryResult } from './inspect-higher-order-query-format';
|
|
2
|
+
import type { BasicQueryData } from '../../base-query-format';
|
|
3
|
+
export declare function executeHigherOrderQuery({ analyzer }: BasicQueryData, queries: readonly InspectHigherOrderQuery[]): Promise<InspectHigherOrderQueryResult>;
|