@eagleoutice/flowr 2.9.2 → 2.9.4

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 (73) hide show
  1. package/README.md +18 -18
  2. package/cli/flowr.js +4 -2
  3. package/control-flow/basic-cfg-guided-visitor.js +10 -4
  4. package/control-flow/cfg-dead-code.js +6 -3
  5. package/control-flow/control-flow-graph.js +3 -2
  6. package/control-flow/simple-visitor.d.ts +2 -1
  7. package/control-flow/simple-visitor.js +6 -7
  8. package/core/print/slice-diff-ansi.js +3 -3
  9. package/dataflow/environments/built-in.d.ts +10 -1
  10. package/dataflow/environments/environment.js +12 -7
  11. package/dataflow/eval/resolve/alias-tracking.d.ts +8 -8
  12. package/dataflow/eval/resolve/alias-tracking.js +33 -34
  13. package/dataflow/eval/resolve/resolve.d.ts +7 -41
  14. package/dataflow/eval/resolve/resolve.js +24 -54
  15. package/dataflow/extractor.js +2 -2
  16. package/dataflow/fn/higher-order-function.d.ts +2 -1
  17. package/dataflow/fn/higher-order-function.js +5 -4
  18. package/dataflow/graph/graph.js +7 -12
  19. package/dataflow/internal/process/functions/call/argument/make-argument.js +1 -2
  20. package/dataflow/internal/process/functions/call/built-in/built-in-register-hook.js +2 -2
  21. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +2 -2
  22. package/dataflow/internal/process/functions/call/built-in/built-in-s-seven-new-generic.js +2 -2
  23. package/dataflow/internal/process/functions/call/built-in/built-in-s-three-dispatch.js +1 -1
  24. package/documentation/doc-util/doc-search.js +2 -2
  25. package/linter/linter-executor.js +1 -2
  26. package/linter/linter-format.d.ts +37 -11
  27. package/linter/linter-format.js +59 -16
  28. package/linter/linter-rules.d.ts +8 -23
  29. package/linter/rules/absolute-path.d.ts +2 -2
  30. package/linter/rules/absolute-path.js +6 -7
  31. package/linter/rules/dataframe-access-validation.d.ts +1 -1
  32. package/linter/rules/dataframe-access-validation.js +3 -4
  33. package/linter/rules/dead-code.d.ts +2 -2
  34. package/linter/rules/dead-code.js +5 -6
  35. package/linter/rules/deprecated-functions.d.ts +4 -7
  36. package/linter/rules/file-path-validity.d.ts +2 -2
  37. package/linter/rules/file-path-validity.js +9 -6
  38. package/linter/rules/function-finder-util.d.ts +8 -11
  39. package/linter/rules/function-finder-util.js +21 -12
  40. package/linter/rules/naming-convention.d.ts +4 -11
  41. package/linter/rules/naming-convention.js +10 -10
  42. package/linter/rules/network-functions.d.ts +5 -8
  43. package/linter/rules/network-functions.js +14 -1
  44. package/linter/rules/seeded-randomness.d.ts +3 -3
  45. package/linter/rules/seeded-randomness.js +5 -5
  46. package/linter/rules/unused-definition.d.ts +2 -2
  47. package/linter/rules/unused-definition.js +13 -14
  48. package/linter/rules/useless-loop.d.ts +3 -3
  49. package/linter/rules/useless-loop.js +4 -4
  50. package/package.json +1 -1
  51. package/project/plugins/file-plugins/files/flowr-namespace-file.js +2 -2
  52. package/queries/catalog/does-call-query/does-call-query-format.js +2 -2
  53. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-format.js +5 -4
  54. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.js +6 -1
  55. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.d.ts +1 -1
  56. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.js +5 -4
  57. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-format.js +4 -3
  58. package/queries/catalog/linter-query/linter-query-format.d.ts +1 -1
  59. package/queries/catalog/linter-query/linter-query-format.js +13 -9
  60. package/queries/query.d.ts +1 -1
  61. package/r-bridge/lang-4.x/ast/parser/main/normalize-meta.d.ts +1 -1
  62. package/r-bridge/lang-4.x/ast/parser/main/normalize-meta.js +2 -2
  63. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +1 -1
  64. package/r-bridge/roxygen2/roxygen-parse.js +1 -1
  65. package/statistics/features/supported/defined-functions/defined-functions.d.ts +1 -1
  66. package/statistics/features/supported/defined-functions/defined-functions.js +4 -4
  67. package/statistics/features/supported/used-functions/used-functions.js +3 -3
  68. package/statistics/features/supported/variables/variables.js +4 -4
  69. package/util/mermaid/dfg.d.ts +0 -5
  70. package/util/mermaid/dfg.js +2 -19
  71. package/util/range.d.ts +137 -54
  72. package/util/range.js +249 -88
  73. package/util/version.js +1 -1
package/README.md CHANGED
@@ -24,7 +24,7 @@ It offers a wide variety of features, for example:
24
24
 
25
25
  ```shell
26
26
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
27
- flowR repl using flowR v2.9.1, R grammar v14 (tree-sitter engine)
27
+ flowR repl using flowR v2.9.3, R grammar v14 (tree-sitter engine)
28
28
  R> :query @linter "read.csv(\"/root/x.txt\")"
29
29
  ```
30
30
 
@@ -80,7 +80,7 @@ It offers a wide variety of features, for example:
80
80
 
81
81
  _Results (prettified and summarized):_
82
82
 
83
- Query: **linter** (2 ms)\
83
+ Query: **linter** (3 ms)\
84
84
     ╰ **Deprecated Functions** (deprecated-functions):\
85
85
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0</code>\
86
86
  &nbsp;&nbsp;&nbsp;╰ **File Path Validity** (file-path-validity):\
@@ -88,7 +88,7 @@ It offers a wide variety of features, for example:
88
88
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
89
89
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 0</code>\
90
90
  &nbsp;&nbsp;&nbsp;╰ **Seeded Randomness** (seeded-randomness):\
91
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 0, processTimeMs: 0</code>\
91
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 0, processTimeMs: 1</code>\
92
92
  &nbsp;&nbsp;&nbsp;╰ **Absolute Paths** (absolute-file-paths):\
93
93
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ certain:\
94
94
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
@@ -96,20 +96,20 @@ It offers a wide variety of features, for example:
96
96
  &nbsp;&nbsp;&nbsp;╰ **Unused Definitions** (unused-definitions):\
97
97
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalConsidered: 0, searchTimeMs: 0, processTimeMs: 0</code>\
98
98
  &nbsp;&nbsp;&nbsp;╰ **Naming Convention** (naming-convention):\
99
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numMatches: 0, numBreak: 0, searchTimeMs: 1, processTimeMs: 0</code>\
99
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numMatches: 0, numBreak: 0, searchTimeMs: 0, processTimeMs: 0</code>\
100
100
  &nbsp;&nbsp;&nbsp;╰ **Network Functions** (network-functions):\
101
101
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0</code>\
102
102
  &nbsp;&nbsp;&nbsp;╰ **Dataframe Access Validation** (dataframe-access-validation):\
103
103
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 0</code>\
104
104
  &nbsp;&nbsp;&nbsp;╰ **Dead Code** (dead-code):\
105
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0</code>\
105
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consideredNodes: 5, searchTimeMs: 1, processTimeMs: 0</code>\
106
106
  &nbsp;&nbsp;&nbsp;╰ **Useless Loops** (useless-loop):\
107
107
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0</code>\
108
- _All queries together required ≈2 ms (1ms accuracy, total 2 ms)_
108
+ _All queries together required ≈3 ms (1ms accuracy, total 3 ms)_
109
109
 
110
110
  <details> <summary style="color:gray">Show Detailed Results as Json</summary>
111
111
 
112
- The analysis required _2.3 ms_ (including parsing and normalization and the query) within the generation environment.
112
+ The analysis required _2.6 ms_ (including parsing and normalization and the query) within the generation environment.
113
113
 
114
114
  In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR.
115
115
  Please consult the [Interface](https://github.com/flowr-analysis/flowr/wiki/Interface) wiki page for more information on how to get those.
@@ -134,7 +134,7 @@ It offers a wide variety of features, for example:
134
134
  "results": [
135
135
  {
136
136
  "involvedId": 3,
137
- "range": [
137
+ "loc": [
138
138
  1,
139
139
  1,
140
140
  1,
@@ -162,7 +162,7 @@ It offers a wide variety of features, for example:
162
162
  "callsWithNonConstantProducers": 0,
163
163
  "callsWithOtherBranchProducers": 0,
164
164
  "searchTimeMs": 0,
165
- "processTimeMs": 0
165
+ "processTimeMs": 1
166
166
  }
167
167
  },
168
168
  "absolute-file-paths": {
@@ -170,7 +170,7 @@ It offers a wide variety of features, for example:
170
170
  {
171
171
  "certainty": "certain",
172
172
  "filePath": "/root/x.txt",
173
- "range": [
173
+ "loc": [
174
174
  1,
175
175
  1,
176
176
  1,
@@ -198,7 +198,7 @@ It offers a wide variety of features, for example:
198
198
  ".meta": {
199
199
  "numMatches": 0,
200
200
  "numBreak": 0,
201
- "searchTimeMs": 1,
201
+ "searchTimeMs": 0,
202
202
  "processTimeMs": 0
203
203
  }
204
204
  },
@@ -225,7 +225,7 @@ It offers a wide variety of features, for example:
225
225
  "results": [],
226
226
  ".meta": {
227
227
  "consideredNodes": 5,
228
- "searchTimeMs": 0,
228
+ "searchTimeMs": 1,
229
229
  "processTimeMs": 0
230
230
  }
231
231
  },
@@ -239,11 +239,11 @@ It offers a wide variety of features, for example:
239
239
  }
240
240
  },
241
241
  ".meta": {
242
- "timing": 2
242
+ "timing": 3
243
243
  }
244
244
  },
245
245
  ".meta": {
246
- "timing": 2
246
+ "timing": 3
247
247
  }
248
248
  }
249
249
  ```
@@ -308,7 +308,7 @@ It offers a wide variety of features, for example:
308
308
 
309
309
  ```shell
310
310
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
311
- flowR repl using flowR v2.9.1, R grammar v14 (tree-sitter engine)
311
+ flowR repl using flowR v2.9.3, R grammar v14 (tree-sitter engine)
312
312
  R> :query @static-slice (11@sum) file://test/testfiles/example.R
313
313
  ```
314
314
 
@@ -356,7 +356,7 @@ It offers a wide variety of features, for example:
356
356
 
357
357
 
358
358
  * 🚀 **fast call-graph, data-, and control-flow graphs**\
359
- Within just [<i><span title="This measurement is automatically fetched from the latest benchmark!">118 ms</span></i> (as of Feb 3, 2026)](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark),
359
+ Within just [<i><span title="This measurement is automatically fetched from the latest benchmark!">121.6 ms</span></i> (as of Feb 4, 2026)](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark),
360
360
  _flowR_ can analyze the data- and control-flow of the average real-world R script. See the [benchmarks](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark) for more information,
361
361
  and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/wiki/dataflow-graph) for more details on the dataflow graphs as well as call graphs.
362
362
 
@@ -392,7 +392,7 @@ It offers a wide variety of features, for example:
392
392
 
393
393
  ```shell
394
394
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
395
- flowR repl using flowR v2.9.1, R grammar v14 (tree-sitter engine)
395
+ flowR repl using flowR v2.9.3, R grammar v14 (tree-sitter engine)
396
396
  R> :dataflow* test/testfiles/example.R
397
397
  ```
398
398
 
@@ -697,7 +697,7 @@ It offers a wide variety of features, for example:
697
697
  ```
698
698
 
699
699
 
700
- (The analysis required _1.9 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
700
+ (The analysis required _2.3 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
701
701
 
702
702
 
703
703
 
package/cli/flowr.js CHANGED
@@ -105,8 +105,10 @@ function hookSignalHandlers(engines) {
105
105
  async function mainRepl() {
106
106
  const config = createConfig();
107
107
  if (options.script) {
108
- const target = scripts_info_1.scripts[options.script].target;
109
- (0, assert_1.guard)(target !== undefined, `Unknown script ${options.script}, pick one of ${(0, flowr_main_options_1.getScriptsText)()}.`);
108
+ const script = scripts_info_1.scripts[options.script];
109
+ (0, assert_1.guard)(script !== undefined, `Unknown script target "${options.script}", pick one of ${(0, flowr_main_options_1.getScriptsText)()}.`);
110
+ const target = script.target;
111
+ (0, assert_1.guard)(target !== undefined, `Unknown script target "${options.script}", pick one of ${(0, flowr_main_options_1.getScriptsText)()}.`);
110
112
  console.log(`Running script '${ansi_1.formatter.format(options.script, { style: 1 /* FontStyles.Bold */ })}'`);
111
113
  log_1.log.debug(`Script maps to "${target}"`);
112
114
  await (0, execute_1.waitOnScript)(`${__dirname}/${target}`, process.argv.slice(3), undefined, true);
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BasicCfgGuidedVisitor = void 0;
4
4
  const control_flow_graph_1 = require("./control-flow-graph");
5
5
  const assert_1 = require("../util/assert");
6
+ const invert_cfg_1 = require("./invert-cfg");
6
7
  /**
7
8
  * In contrast to {@link visitCfgInOrder} and {@link visitCfgInReverseOrder}, this visitor is not a simple visitor
8
9
  * and serves as the basis for a variety of more complicated visiting orders of the control flow graph.
@@ -31,10 +32,15 @@ class BasicCfgGuidedVisitor {
31
32
  }
32
33
  startVisitor(start) {
33
34
  const graph = this.config.controlFlow.graph;
34
- const getNext = this.config.defaultVisitingOrder === 'forward' ?
35
- (node) => graph.ingoingEdges(node)?.keys().toArray().toReversed() :
36
- (node) => graph.outgoingEdges(node)?.keys().toArray();
37
- const stack = [...start];
35
+ let getNext;
36
+ if (this.config.defaultVisitingOrder === 'forward') {
37
+ const inverseGraph = (0, invert_cfg_1.invertCfg)(graph);
38
+ getNext = (node) => inverseGraph.outgoingEdges(node)?.keys().toArray().reverse();
39
+ }
40
+ else {
41
+ getNext = (node) => graph.outgoingEdges(node)?.keys();
42
+ }
43
+ const stack = Array.from(start);
38
44
  while (stack.length > 0) {
39
45
  const current = stack.pop();
40
46
  if (!this.visitNode(current)) {
@@ -9,10 +9,12 @@ const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-functi
9
9
  const general_1 = require("../dataflow/eval/values/general");
10
10
  const r_value_1 = require("../dataflow/eval/values/r-value");
11
11
  const simple_visitor_1 = require("./simple-visitor");
12
+ const invert_cfg_1 = require("./invert-cfg");
12
13
  class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor {
13
14
  cachedConditions = new Map();
14
15
  cachedStatements = new Map();
15
16
  inTry = new Set();
17
+ invertedCfg;
16
18
  getValue(id) {
17
19
  const has = this.cachedConditions.get(id);
18
20
  if (has) {
@@ -39,6 +41,7 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
39
41
  this.cachedConditions.set(id, value ? logic_1.Ternary.Always : logic_1.Ternary.Never);
40
42
  }
41
43
  startVisitor() {
44
+ this.invertedCfg = (0, invert_cfg_1.invertCfg)(this.config.controlFlow.graph);
42
45
  for (const [from, targets] of this.config.controlFlow.graph.edges()) {
43
46
  for (const [target, edge] of targets) {
44
47
  if (edge.label === 1 /* CfgEdgeType.Cd */) {
@@ -53,7 +56,7 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
53
56
  else if (edge.label === 0 /* CfgEdgeType.Fd */ && this.isUnconditionalJump(target)) {
54
57
  // for each unconditional jump, we find the corresponding end/exit nodes and remove any flow edges
55
58
  for (const end of this.getCfgVertex(target)?.end ?? []) {
56
- for (const [target, edge] of this.config.controlFlow.graph.ingoingEdges(end) ?? []) {
59
+ for (const [target, edge] of this.invertedCfg.outgoingEdges(end) ?? []) {
57
60
  if (edge.label === 0 /* CfgEdgeType.Fd */) {
58
61
  this.config.controlFlow.graph.removeEdge(target, end);
59
62
  }
@@ -130,7 +133,7 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
130
133
  }
131
134
  this.inTry.add(n);
132
135
  return false;
133
- });
136
+ }, this.invertedCfg);
134
137
  }
135
138
  }
136
139
  /** Breaks unsatisfiable control dependencies */
@@ -144,7 +147,7 @@ function cfgAnalyzeDeadCode(cfg, info) {
144
147
  normalizedAst: info.ast,
145
148
  dfg: info.dfg,
146
149
  ctx: info.ctx,
147
- defaultVisitingOrder: 'forward',
150
+ defaultVisitingOrder: 'backward'
148
151
  });
149
152
  visitor.start();
150
153
  return cfg;
@@ -132,8 +132,9 @@ class ControlFlowGraph {
132
132
  ingoingEdges(id) {
133
133
  const edges = new Map();
134
134
  for (const [source, outgoing] of this.edgeInformation.entries()) {
135
- if (outgoing.has(id)) {
136
- edges.set(source, outgoing.get(id));
135
+ const o = outgoing.get(id);
136
+ if (o) {
137
+ edges.set(source, o);
137
138
  }
138
139
  }
139
140
  return edges;
@@ -16,11 +16,12 @@ export declare function visitCfgInReverseOrder(graph: ControlFlowGraph, startNod
16
16
  * @param graph - The control flow graph.
17
17
  * @param startNodes - The nodes to start the traversal from.
18
18
  * @param visitor - The visitor function to call for each node, if you return true the traversal from this node will be stopped.
19
+ * @param invertedCfg - Optionally provide an inverted control flow graph, if not provided the function will create one by inverting the given graph, which can be expensive for large graphs.
19
20
  *
20
21
  * This function is of type {@link SimpleCfgVisitor}.
21
22
  * @see {@link visitCfgInReverseOrder} for a traversal in reversed order
22
23
  */
23
- export declare function visitCfgInOrder(graph: ControlFlowGraph, startNodes: readonly NodeId[], visitor: (node: NodeId) => boolean | void): Set<NodeId>;
24
+ export declare function visitCfgInOrder(graph: ControlFlowGraph, startNodes: readonly NodeId[], visitor: (node: NodeId) => boolean | void, invertedCfg?: ControlFlowGraph): Set<NodeId>;
24
25
  /**
25
26
  * Check if a node can reach another node in the control flow graph.
26
27
  */
@@ -35,11 +35,9 @@ visitor) {
35
35
  queue = queue.concat(get.elems.toReversed().map(e => e.id));
36
36
  }
37
37
  }
38
- const incoming = graph.outgoingEdges(current);
39
- if (incoming) {
40
- for (const c of incoming.keys()) {
41
- queue.push(c);
42
- }
38
+ const incoming = graph.outgoingEdges(current) ?? [];
39
+ for (const c of incoming.keys()) {
40
+ queue.push(c);
43
41
  }
44
42
  }
45
43
  }
@@ -48,17 +46,18 @@ visitor) {
48
46
  * @param graph - The control flow graph.
49
47
  * @param startNodes - The nodes to start the traversal from.
50
48
  * @param visitor - The visitor function to call for each node, if you return true the traversal from this node will be stopped.
49
+ * @param invertedCfg - Optionally provide an inverted control flow graph, if not provided the function will create one by inverting the given graph, which can be expensive for large graphs.
51
50
  *
52
51
  * This function is of type {@link SimpleCfgVisitor}.
53
52
  * @see {@link visitCfgInReverseOrder} for a traversal in reversed order
54
53
  */
55
54
  function visitCfgInOrder(graph, startNodes,
56
55
  // eslint-disable-next-line @typescript-eslint/no-invalid-void-type -- void is used to indicate that the return value is ignored/we never stop
57
- visitor) {
56
+ visitor, invertedCfg) {
58
57
  const visited = new Set();
59
58
  let queue = startNodes.slice();
60
59
  const hasBb = graph.mayHaveBasicBlocks();
61
- const g = (0, invert_cfg_1.invertCfg)(graph);
60
+ const g = invertedCfg ?? (0, invert_cfg_1.invertCfg)(graph);
62
61
  while (queue.length > 0) {
63
62
  const current = queue.shift();
64
63
  if (visited.has(current)) {
@@ -9,11 +9,11 @@ function grayOut() {
9
9
  }
10
10
  function mergeJointRangesInSorted(loc) {
11
11
  return loc.reduce((acc, curr) => {
12
- if ((0, range_1.rangesOverlap)(acc[acc.length - 1].location, curr.location)) {
12
+ if (range_1.SourceRange.overlap(acc[acc.length - 1].location, curr.location)) {
13
13
  return [
14
14
  ...acc.slice(0, -1), {
15
15
  selected: curr.selected || acc[acc.length - 1].selected,
16
- location: (0, range_1.mergeRanges)([acc[acc.length - 1].location, curr.location])
16
+ location: range_1.SourceRange.merge([acc[acc.length - 1].location, curr.location])
17
17
  }
18
18
  ];
19
19
  }
@@ -37,7 +37,7 @@ function sliceDiffAnsi(slice, normalized, criteriaIds, originalCode) {
37
37
  return `${grayOut()}${originalCode}${ansi_1.ansiFormatter.reset()}`;
38
38
  }
39
39
  // we sort all locations from back to front so that replacements do not screw up the indices
40
- importantLocations.sort((a, b) => -(0, range_1.rangeCompare)(a.location, b.location));
40
+ importantLocations.sort((a, b) => -range_1.SourceRange.compare(a.location, b.location));
41
41
  // we need to merge all ranges that overlap, otherwise even reversed traversal can still crew us up
42
42
  importantLocations = mergeJointRangesInSorted(importantLocations);
43
43
  const lines = originalCode.split('\n');
@@ -83,7 +83,16 @@ export interface DefaultBuiltInProcessorConfiguration extends ForceArguments {
83
83
  */
84
84
  readonly useAsProcessor?: BuiltInProcName;
85
85
  }
86
- export type BuiltInEvalHandler = (resolve: VariableResolve, a: RNodeWithParent, ctx: ReadOnlyFlowrAnalyzerContext, env?: REnvironmentInformation, graph?: DataflowGraph, map?: AstIdMap) => Value;
86
+ export interface BuiltInEvalHandlerArgs {
87
+ resolve: VariableResolve;
88
+ node: RNodeWithParent;
89
+ ctx: ReadOnlyFlowrAnalyzerContext;
90
+ environment?: REnvironmentInformation;
91
+ graph?: DataflowGraph;
92
+ idMap?: AstIdMap;
93
+ blocked?: Set<NodeId>;
94
+ }
95
+ export type BuiltInEvalHandler = (args: BuiltInEvalHandlerArgs) => Value;
87
96
  declare function defaultBuiltInProcessor<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, { returnsNthArgument, useAsProcessor, forceArgs, readAllArguments, cfg, hasUnknownSideEffects, treatAsFnCall }: DefaultBuiltInProcessorConfiguration): DataflowInformation;
88
97
  /**
89
98
  * This contains all names of built-in function handlers and origins
@@ -110,7 +110,7 @@ class Environment {
110
110
  }
111
111
  // navigate to parent until either before built-in or matching namespace
112
112
  const newEnvironment = this.clone(false);
113
- const current = newEnvironment;
113
+ let current = newEnvironment;
114
114
  do {
115
115
  if (current.n === ns) {
116
116
  current.define(definition);
@@ -119,6 +119,7 @@ class Environment {
119
119
  else if (current.parent && !current.parent.builtInEnv) {
120
120
  // clone parent
121
121
  current.parent = current.parent.clone(false);
122
+ current = current.parent;
122
123
  }
123
124
  else {
124
125
  break;
@@ -163,7 +164,7 @@ class Environment {
163
164
  * This always recurses parents.
164
165
  */
165
166
  overwrite(other, applyCds) {
166
- if (!other || this.builtInEnv) {
167
+ if (this.builtInEnv || this === other || !other) {
167
168
  return this;
168
169
  }
169
170
  const map = new Map(this.memory);
@@ -171,25 +172,29 @@ class Environment {
171
172
  const hasMaybe = applyCds === undefined ? values.length === 0 || values.some(v => v.cds !== undefined) : true;
172
173
  if (hasMaybe) {
173
174
  const old = map.get(key);
175
+ if (!old && applyCds === undefined) {
176
+ map.set(key, values);
177
+ continue;
178
+ }
174
179
  // we need to make a copy to avoid side effects for old reference in other environments
175
- const updatedOld = old?.slice() ?? [];
180
+ const updated = old?.slice() ?? [];
176
181
  for (const v of values) {
177
182
  const { nodeId, definedAt } = v;
178
- const index = updatedOld.find(o => o.nodeId === nodeId && o.definedAt === definedAt);
183
+ const index = updated.find(o => o.nodeId === nodeId && o.definedAt === definedAt);
179
184
  if (index) {
180
185
  continue;
181
186
  }
182
187
  if (applyCds === undefined) {
183
- updatedOld.push(v);
188
+ updated.push(v);
184
189
  }
185
190
  else {
186
- updatedOld.push({
191
+ updated.push({
187
192
  ...v,
188
193
  cds: v.cds ? applyCds.concat(v.cds) : applyCds.slice()
189
194
  });
190
195
  }
191
196
  }
192
- map.set(key, updatedOld);
197
+ map.set(key, updated);
193
198
  }
194
199
  else {
195
200
  map.set(key, values);
@@ -2,7 +2,7 @@ import { VariableResolve } from '../../../config';
2
2
  import type { AstIdMap, RNodeWithParent } from '../../../r-bridge/lang-4.x/ast/model/processing/decorate';
3
3
  import { type NodeId } from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
4
4
  import { type REnvironmentInformation } from '../../environments/environment';
5
- import { type Identifier } from '../../environments/identifier';
5
+ import { Identifier } from '../../environments/identifier';
6
6
  import type { DataflowGraph } from '../../graph/graph';
7
7
  import { type Lift, type Value, type ValueSet } from '../values/r-value';
8
8
  import type { ReadOnlyFlowrAnalyzerContext } from '../../../project/context/flowr-analyzer-context';
@@ -20,6 +20,8 @@ export interface ResolveInfo {
20
20
  resolve?: VariableResolve;
21
21
  /** Context used for resolving */
22
22
  ctx: ReadOnlyFlowrAnalyzerContext;
23
+ /** If set, the ids that should not be considered during resolution (=&gt; top) */
24
+ blocked?: Set<NodeId>;
23
25
  }
24
26
  /**
25
27
  * Gets the definitions / aliases of a node
@@ -54,22 +56,20 @@ export declare function getAliases(sourceIds: readonly NodeId[], dataflow: Dataf
54
56
  * @param full - Whether to track aliases on resolve
55
57
  * @param resolve - Variable resolve mode
56
58
  * @param ctx - Context used for clean environment
59
+ * @param blocked - If set, the ids that should not be considered during resolution (=&gt;top)
57
60
  */
58
- export declare function resolveIdToValue(id: NodeId | RNodeWithParent | undefined, { environment, graph, idMap, full, resolve, ctx }: ResolveInfo): ResolveResult;
61
+ export declare function resolveIdToValue(id: NodeId | RNodeWithParent | undefined, { environment, graph, idMap, full, resolve, ctx, blocked }: ResolveInfo): ResolveResult;
59
62
  /**
60
63
  * Please use {@link resolveIdToValue}
61
64
  *
62
65
  * Uses the aliases that were tracked in the environments (by the
63
66
  * {@link getAliases} function) to resolve a node to a value.
64
- * @param resolve - Variable resolve mode
65
67
  * @param identifier - Identifier to resolve
66
- * @param use - Environment to use
67
- * @param ctx - analysis context
68
- * @param graph - dataflow graph
69
- * @param idMap - id map of Dataflow graph
68
+ * @param environment - Environment to use
69
+ * @param r - Resolve information (env, ctx, ...)
70
70
  * @returns Value of Identifier or Top
71
71
  */
72
- export declare function trackAliasInEnvironments(resolve: VariableResolve, identifier: Identifier | undefined, use: REnvironmentInformation, ctx: ReadOnlyFlowrAnalyzerContext, graph?: DataflowGraph, idMap?: AstIdMap): ResolveResult;
72
+ export declare function trackAliasInEnvironments(identifier: Identifier | undefined, environment: REnvironmentInformation, { blocked, idMap, resolve, ctx, graph }: Omit<ResolveInfo, 'environment'>): ResolveResult;
73
73
  /**
74
74
  * Please use {@link resolveIdToValue}
75
75
  *
@@ -117,39 +117,44 @@ function getAliases(sourceIds, dataflow, environment) {
117
117
  * @param full - Whether to track aliases on resolve
118
118
  * @param resolve - Variable resolve mode
119
119
  * @param ctx - Context used for clean environment
120
+ * @param blocked - If set, the ids that should not be considered during resolution (=&gt;top)
120
121
  */
121
- function resolveIdToValue(id, { environment, graph, idMap, full = true, resolve, ctx }) {
122
+ function resolveIdToValue(id, { environment, graph, idMap, full = true, resolve, ctx, blocked }) {
122
123
  const variableResolve = resolve ?? ctx.config.solver.variables;
124
+ blocked ??= new Set();
123
125
  if (id === undefined) {
124
126
  return r_value_1.Top;
125
127
  }
126
128
  idMap ??= graph?.idMap;
127
129
  const node = typeof id === 'object' ? id : idMap?.get(id);
128
- if (node === undefined) {
130
+ if (node === undefined || blocked.has(node.info.id)) {
129
131
  return r_value_1.Top;
130
132
  }
133
+ blocked.add(node.info.id);
131
134
  switch (node.type) {
132
135
  case type_1.RType.Argument:
133
136
  if (node.value) {
134
- return resolveIdToValue(node.value.info.id, { environment, graph, idMap, full, resolve: variableResolve, ctx });
137
+ return resolveIdToValue(node.value.info.id, { environment, graph, idMap, full, resolve: variableResolve, ctx, blocked });
135
138
  }
136
139
  // eslint-disable-next-line no-fallthrough
137
140
  case type_1.RType.Symbol:
138
- if (environment) {
139
- return full ? trackAliasInEnvironments(variableResolve, node.lexeme, environment, ctx, graph, idMap) : r_value_1.Top;
140
- }
141
- else if (graph && resolve === config_1.VariableResolve.Alias) {
142
- return full ? trackAliasesInGraph(node.info.id, graph, ctx, idMap) : r_value_1.Top;
143
- }
144
- else {
145
- return r_value_1.Top;
141
+ if (full) {
142
+ if (environment) {
143
+ return trackAliasInEnvironments(identifier_1.Identifier.toString(node.content), environment, { idMap, resolve: variableResolve, ctx, graph, blocked });
144
+ }
145
+ else if (graph && resolve === config_1.VariableResolve.Alias) {
146
+ return trackAliasesInGraph(node.info.id, graph, ctx, idMap);
147
+ }
146
148
  }
149
+ return r_value_1.Top;
147
150
  case type_1.RType.FunctionDefinition:
148
151
  return (0, set_constants_1.setFrom)({ type: 'function-definition' });
149
152
  case type_1.RType.FunctionCall:
150
153
  case type_1.RType.BinaryOp:
151
154
  case type_1.RType.UnaryOp:
152
- return (0, set_constants_1.setFrom)((0, resolve_1.resolveNode)(variableResolve, node, ctx, environment, graph, idMap));
155
+ return (0, set_constants_1.setFrom)((0, resolve_1.resolveNode)({
156
+ resolve: variableResolve, node, ctx, environment, graph, idMap, blocked
157
+ }));
153
158
  case type_1.RType.String:
154
159
  case type_1.RType.Number:
155
160
  case type_1.RType.Logical:
@@ -163,19 +168,16 @@ function resolveIdToValue(id, { environment, graph, idMap, full = true, resolve,
163
168
  *
164
169
  * Uses the aliases that were tracked in the environments (by the
165
170
  * {@link getAliases} function) to resolve a node to a value.
166
- * @param resolve - Variable resolve mode
167
171
  * @param identifier - Identifier to resolve
168
- * @param use - Environment to use
169
- * @param ctx - analysis context
170
- * @param graph - dataflow graph
171
- * @param idMap - id map of Dataflow graph
172
+ * @param environment - Environment to use
173
+ * @param r - Resolve information (env, ctx, ...)
172
174
  * @returns Value of Identifier or Top
173
175
  */
174
- function trackAliasInEnvironments(resolve, identifier, use, ctx, graph, idMap) {
176
+ function trackAliasInEnvironments(identifier, environment, { blocked, idMap, resolve = config_1.VariableResolve.Alias, ctx, graph }) {
175
177
  if (identifier === undefined) {
176
178
  return r_value_1.Top;
177
179
  }
178
- const defs = (0, resolve_by_name_1.resolveByNameAnyType)(identifier, use);
180
+ const defs = (0, resolve_by_name_1.resolveByNameAnyType)(identifier, environment);
179
181
  if (defs === undefined) {
180
182
  return r_value_1.Top;
181
183
  }
@@ -195,7 +197,7 @@ function trackAliasInEnvironments(resolve, identifier, use, ctx, graph, idMap) {
195
197
  for (const alias of def.value) {
196
198
  const definitionOfAlias = idMap?.get(alias);
197
199
  if (definitionOfAlias !== undefined) {
198
- const value = (0, resolve_1.resolveNode)(resolve, definitionOfAlias, ctx, use, graph, idMap);
200
+ const value = (0, resolve_1.resolveNode)({ resolve, node: definitionOfAlias, ctx, environment, graph, idMap, blocked });
199
201
  if ((0, r_value_1.isTop)(value)) {
200
202
  return r_value_1.Top;
201
203
  }
@@ -252,7 +254,7 @@ function isNestedInLoop(node, ast) {
252
254
  if (parentNode === undefined) {
253
255
  return false;
254
256
  }
255
- if (parentNode.type === type_1.RType.WhileLoop || parentNode.type === type_1.RType.RepeatLoop) {
257
+ if (parentNode.type === type_1.RType.WhileLoop || parentNode.type === type_1.RType.RepeatLoop || parentNode.type === type_1.RType.ForLoop) {
256
258
  return true;
257
259
  }
258
260
  return isNestedInLoop(parentNode, ast);
@@ -273,7 +275,7 @@ function trackAliasesInGraph(id, graph, ctx, idMap) {
273
275
  }
274
276
  idMap ??= graph.idMap;
275
277
  (0, assert_1.guard)(idMap !== undefined, 'The ID map is required to get the lineage of a node');
276
- const queue = new visiting_queue_1.VisitingQueue(25);
278
+ const queue = new visiting_queue_1.VisitingQueue(10);
277
279
  const clean = ctx.env.makeCleanEnv();
278
280
  const cleanFingerprint = ctx.env.getCleanEnvFingerprint();
279
281
  queue.add(id, clean, cleanFingerprint, false);
@@ -281,11 +283,10 @@ function trackAliasesInGraph(id, graph, ctx, idMap) {
281
283
  const resultIds = [];
282
284
  while (queue.nonEmpty()) {
283
285
  const { id, baseEnvironment } = queue.next();
284
- const res = graph.get(id);
285
- if (!res) {
286
+ const vertex = graph.getVertex(id);
287
+ if (!vertex) {
286
288
  continue;
287
289
  }
288
- const [vertex, outgoingEdges] = res;
289
290
  const cds = vertex.cds;
290
291
  for (const cd of cds ?? []) {
291
292
  const target = graph.idMap?.get(cd.id);
@@ -303,25 +304,23 @@ function trackAliasesInGraph(id, graph, ctx, idMap) {
303
304
  if (forceTop) {
304
305
  break;
305
306
  }
306
- if (vertex.tag === vertex_1.VertexType.Value) {
307
- resultIds.push(id);
308
- continue;
309
- }
310
- else if (vertex.tag === vertex_1.VertexType.FunctionDefinition) {
307
+ const t = vertex.tag;
308
+ if (t === vertex_1.VertexType.Value || t === vertex_1.VertexType.FunctionDefinition) {
311
309
  resultIds.push(id);
312
310
  continue;
313
311
  }
314
- const isFn = vertex.tag === vertex_1.VertexType.FunctionCall;
312
+ const isFn = t === vertex_1.VertexType.FunctionCall;
313
+ const outgoingEdges = graph.outgoingEdges(id) ?? [];
315
314
  // travel all read and defined-by edges
316
- for (const [targetId, edge] of outgoingEdges) {
315
+ for (const [targetId, { types }] of outgoingEdges) {
317
316
  if (isFn) {
318
- if (edge.types === edge_1.EdgeType.Returns || edge.types === edge_1.EdgeType.DefinedByOnCall || edge.types === edge_1.EdgeType.DefinedBy) {
317
+ if (types === edge_1.EdgeType.Returns || types === edge_1.EdgeType.DefinedByOnCall || types === edge_1.EdgeType.DefinedBy) {
319
318
  queue.add(targetId, baseEnvironment, cleanFingerprint, false);
320
319
  }
321
320
  continue;
322
321
  }
323
322
  // currently, they have to be exact!
324
- if (edge.types === edge_1.EdgeType.Reads || edge.types === edge_1.EdgeType.DefinedBy || edge.types === edge_1.EdgeType.DefinedByOnCall) {
323
+ if (types === edge_1.EdgeType.Reads || types === edge_1.EdgeType.DefinedBy || types === edge_1.EdgeType.DefinedByOnCall) {
325
324
  queue.add(targetId, baseEnvironment, cleanFingerprint, false);
326
325
  }
327
326
  }