@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.
Files changed (71) hide show
  1. package/README.md +36 -40
  2. package/abstract-interpretation/data-frame/absint-visitor.d.ts +1 -1
  3. package/abstract-interpretation/data-frame/absint-visitor.js +1 -1
  4. package/cli/flowr.js +8 -4
  5. package/cli/repl/commands/repl-cfg.js +4 -4
  6. package/cli/repl/commands/repl-commands.js +2 -2
  7. package/cli/repl/commands/repl-dataflow.js +4 -4
  8. package/cli/repl/commands/repl-execute.d.ts +1 -1
  9. package/cli/repl/commands/repl-execute.js +8 -8
  10. package/cli/repl/commands/repl-lineage.d.ts +2 -2
  11. package/cli/repl/commands/repl-lineage.js +11 -12
  12. package/cli/repl/commands/repl-main.d.ts +4 -7
  13. package/cli/repl/commands/repl-normalize.js +2 -2
  14. package/cli/repl/commands/repl-parse.js +3 -2
  15. package/cli/repl/commands/repl-query.js +3 -3
  16. package/cli/repl/commands/repl-quit.js +1 -1
  17. package/cli/repl/commands/repl-version.d.ts +1 -16
  18. package/cli/repl/commands/repl-version.js +2 -19
  19. package/cli/repl/core.d.ts +9 -9
  20. package/cli/repl/core.js +14 -20
  21. package/cli/repl/print-version.js +2 -2
  22. package/cli/repl/server/connection.js +6 -2
  23. package/cli/repl/server/messages/message-hello.d.ts +1 -1
  24. package/cli/repl/server/server.js +2 -2
  25. package/core/steps/all/core/20-dataflow.d.ts +3 -1
  26. package/core/steps/pipeline/default-pipelines.d.ts +66 -50
  27. package/dataflow/environments/default-builtin-config.js +8 -0
  28. package/dataflow/eval/resolve/alias-tracking.js +2 -0
  29. package/dataflow/eval/resolve/resolve.js +3 -0
  30. package/dataflow/eval/values/r-value.d.ts +4 -1
  31. package/dataflow/eval/values/r-value.js +2 -0
  32. package/dataflow/extractor.d.ts +4 -1
  33. package/dataflow/extractor.js +7 -5
  34. package/dataflow/fn/higher-order-function.d.ts +9 -0
  35. package/dataflow/fn/higher-order-function.js +75 -0
  36. package/documentation/doc-util/doc-repl.js +5 -2
  37. package/documentation/print-dataflow-graph-wiki.js +2 -2
  38. package/documentation/print-query-wiki.js +20 -0
  39. package/documentation/print-readme.js +1 -1
  40. package/package.json +1 -1
  41. package/project/cache/flowr-analyzer-cache.d.ts +6 -5
  42. package/project/cache/flowr-analyzer-cache.js +21 -13
  43. package/project/cfg-kind.d.ts +17 -0
  44. package/project/cfg-kind.js +22 -0
  45. package/project/context/abstract-flowr-analyzer-context.d.ts +4 -0
  46. package/project/context/flowr-analyzer-context.d.ts +6 -0
  47. package/project/context/flowr-analyzer-context.js +11 -0
  48. package/project/context/flowr-analyzer-dependencies-context.d.ts +1 -0
  49. package/project/context/flowr-analyzer-dependencies-context.js +4 -0
  50. package/project/context/flowr-analyzer-files-context.d.ts +1 -0
  51. package/project/context/flowr-analyzer-files-context.js +4 -0
  52. package/project/context/flowr-analyzer-loading-order-context.d.ts +1 -0
  53. package/project/context/flowr-analyzer-loading-order-context.js +6 -0
  54. package/project/flowr-analyzer.d.ts +19 -18
  55. package/project/flowr-analyzer.js +15 -8
  56. package/queries/catalog/call-context-query/call-context-query-executor.js +2 -1
  57. package/queries/catalog/config-query/config-query-format.d.ts +1 -1
  58. package/queries/catalog/control-flow-query/control-flow-query-executor.js +2 -1
  59. package/queries/catalog/df-shape-query/df-shape-query-executor.js +1 -1
  60. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.d.ts +3 -0
  61. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.js +45 -0
  62. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.d.ts +22 -0
  63. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.js +32 -0
  64. package/queries/query.d.ts +10 -2
  65. package/queries/query.js +2 -0
  66. package/r-bridge/parser.d.ts +7 -0
  67. package/search/search-executor/search-enrichers.js +2 -1
  68. package/util/r-value.d.ts +1 -1
  69. package/util/r-value.js +2 -0
  70. package/util/version.d.ts +17 -0
  71. 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 config_1 = require("../../config");
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 (0, core_1.replProcessAnswer)(config_1.defaultConfigOptions, collectingOutput, command.command, parser, options?.allowRSessionAccess ?? false);
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 === 8, () => 'Update Dataflow Documentation!');
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.6.0",
3
+ "version": "2.6.1",
4
4
  "description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "repository": {
@@ -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 useDataflow - Whether to use the dataflow graph for the creation of the CFG.
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, useDataflow: boolean, simplifications: readonly CfgSimplificationPassName[] | undefined): Promise<ControlFlowInformation>;
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 useDataflow - Whether to use the dataflow graph for the creation of the CFG.
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(useDataflow: boolean, simplifications: readonly CfgSimplificationPassName[] | undefined): ControlFlowInformation | undefined;
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 useDataflow - Whether to use the dataflow graph for the creation of the CFG.
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, useDataflow, simplifications) {
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, useDataflow]);
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 dataflow = undefined;
135
- if (useDataflow) {
136
- /* if force is active, it will have triggered with normalize */
137
- dataflow = await this.dataflow();
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
- const result = simplifications.length === 0 && !useDataflow ? (0, extract_cfg_1.extractCfgQuick)(normalized) :
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 useDataflow - Whether to use the dataflow graph for the creation of the CFG.
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(useDataflow, simplifications) {
152
- return this.controlFlowCache.simplified.get([simplifications ?? [], useDataflow]);
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, KnownParserName, ParseStepOutput } from '../r-bridge/parser';
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
- parserName(): string;
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 useDataflow - Whether to use the dataflow graph for the creation of the CFG.
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[], useDataflow?: boolean, force?: boolean): Promise<ControlFlowInformation>;
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
- parserName(): KnownParserName;
113
- parse(force?: boolean): ReturnType<typeof this.cache.parse>;
114
- normalize(force?: boolean): ReturnType<typeof this.cache.normalize>;
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[], useDataflow?: boolean, force?: boolean): Promise<ControlFlowInformation>;
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, useDataflow, force) {
62
- return this.cache.controlflow(force, useDataflow ?? false, simplifications);
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([], true);
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[]) => boolean;
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, true);
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>;