@eagleoutice/flowr 2.9.3 → 2.9.5

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 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.2, R grammar v14 (tree-sitter engine)
27
+ flowR repl using flowR v2.9.4, R grammar v14 (tree-sitter engine)
28
28
  R> :query @linter "read.csv(\"/root/x.txt\")"
29
29
  ```
30
30
 
@@ -82,17 +82,17 @@ It offers a wide variety of features, for example:
82
82
 
83
83
  Query: **linter** (3 ms)\
84
84
     ╰ **Deprecated Functions** (deprecated-functions):\
85
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0</code>\
85
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0</code>\
86
86
  &nbsp;&nbsp;&nbsp;╰ **File Path Validity** (file-path-validity):\
87
87
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ certain:\
88
88
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
89
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 0</code>\
89
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 1, processTimeMs: 0</code>\
90
90
  &nbsp;&nbsp;&nbsp;╰ **Seeded Randomness** (seeded-randomness):\
91
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>\
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\
95
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalConsidered: 1, totalUnknown: 0, searchTimeMs: 0, processTimeMs: 0</code>\
95
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalConsidered: 1, totalUnknown: 0, searchTimeMs: 1, processTimeMs: 0</code>\
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):\
@@ -109,7 +109,7 @@ It offers a wide variety of features, for example:
109
109
 
110
110
  <details> <summary style="color:gray">Show Detailed Results as Json</summary>
111
111
 
112
- The analysis required _2.9 ms_ (including parsing and normalization and the query) within the generation environment.
112
+ The analysis required _3.5 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.
@@ -126,7 +126,7 @@ It offers a wide variety of features, for example:
126
126
  ".meta": {
127
127
  "totalCalls": 0,
128
128
  "totalFunctionDefinitions": 0,
129
- "searchTimeMs": 1,
129
+ "searchTimeMs": 0,
130
130
  "processTimeMs": 0
131
131
  }
132
132
  },
@@ -149,7 +149,7 @@ It offers a wide variety of features, for example:
149
149
  "totalUnknown": 0,
150
150
  "totalWritesBeforeAlways": 0,
151
151
  "totalValid": 0,
152
- "searchTimeMs": 0,
152
+ "searchTimeMs": 1,
153
153
  "processTimeMs": 0
154
154
  }
155
155
  },
@@ -181,7 +181,7 @@ It offers a wide variety of features, for example:
181
181
  ".meta": {
182
182
  "totalConsidered": 1,
183
183
  "totalUnknown": 0,
184
- "searchTimeMs": 0,
184
+ "searchTimeMs": 1,
185
185
  "processTimeMs": 0
186
186
  }
187
187
  },
@@ -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.2, R grammar v14 (tree-sitter engine)
311
+ flowR repl using flowR v2.9.4, R grammar v14 (tree-sitter engine)
312
312
  R> :query @static-slice (11@sum) file://test/testfiles/example.R
313
313
  ```
314
314
 
@@ -322,7 +322,7 @@ It offers a wide variety of features, for example:
322
322
  N <- 10
323
323
  for(i in 1:(N-1)) sum <- sum + i + w
324
324
  sum
325
- All queries together required ≈3 ms (1ms accuracy, total 3 ms)
325
+ All queries together required ≈3 ms (1ms accuracy, total 4 ms)
326
326
  ```
327
327
 
328
328
 
@@ -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!">117 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!">111.1 ms</span></i> (as of Feb 5, 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.2, R grammar v14 (tree-sitter engine)
395
+ flowR repl using flowR v2.9.4, 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 _2.0 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 _4.5 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)) {
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.uniqueMergeValuesInDefinitions = uniqueMergeValuesInDefinitions;
4
4
  exports.appendEnvironment = appendEnvironment;
5
- const assert_1 = require("../../util/assert");
5
+ const scoping_1 = require("./scoping");
6
6
  /**
7
7
  * Merges two arrays of identifier definitions, ensuring uniqueness based on `nodeId` and `definedAt`.
8
8
  */
@@ -23,7 +23,14 @@ function appendEnvironment(base, next) {
23
23
  else if (next === undefined) {
24
24
  return base;
25
25
  }
26
- (0, assert_1.guard)(base.level === next.level, 'environments must have the same level to be handled, it is up to the caller to ensure that');
26
+ if (base.level !== next.level) {
27
+ while (next.level < base.level) {
28
+ next = (0, scoping_1.pushLocalEnvironment)(next);
29
+ }
30
+ while (next.level > base.level) {
31
+ base = (0, scoping_1.pushLocalEnvironment)(base);
32
+ }
33
+ }
27
34
  return {
28
35
  current: base.current.append(next.current),
29
36
  level: base.level,
@@ -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 || this.n !== other.n) {
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);
@@ -206,7 +211,7 @@ class Environment {
206
211
  * This always recurses parents.
207
212
  */
208
213
  append(other) {
209
- if (!other || this.builtInEnv) {
214
+ if (!other || this.builtInEnv || this === other || this.n !== other.n) {
210
215
  return this;
211
216
  }
212
217
  const map = new Map(this.memory);
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.overwriteEnvironment = overwriteEnvironment;
4
- const assert_1 = require("../../util/assert");
4
+ const scoping_1 = require("./scoping");
5
5
  /**
6
6
  * Assumes, that all definitions within next replace those within base (given the same name).
7
7
  * <b>But</b> if all definitions within next are maybe, then they are appended to the base definitions (updating them to be `maybe` from now on as well), similar to {@link appendEnvironment}.
@@ -14,7 +14,14 @@ function overwriteEnvironment(base, next, applyCds) {
14
14
  else if (next === undefined) {
15
15
  return base;
16
16
  }
17
- (0, assert_1.guard)(next.level === base.level, `cannot overwrite environments with differently nested local scopes, base ${base.level} vs. next ${next.level}. This should not happen.`);
17
+ if (base.level !== next.level) {
18
+ while (next.level < base.level) {
19
+ next = (0, scoping_1.pushLocalEnvironment)(next);
20
+ }
21
+ while (next.level > base.level) {
22
+ base = (0, scoping_1.pushLocalEnvironment)(base);
23
+ }
24
+ }
18
25
  return {
19
26
  current: base.current.overwrite(next.current, applyCds),
20
27
  level: base.level
@@ -6,5 +6,6 @@ import type { ReadOnlyFlowrAnalyzerContext } from '../../project/context/flowr-a
6
6
  * either takes a function as an argument or (may) returns a function.
7
7
  * If the return is an identity, e.g., `function(x) x`, this is not considered higher-order,
8
8
  * if no function is passed as an argument.
9
+ * Please note that inspecting higher order functions can be sped up (if queries multiple times) by providing an inverted graph as well!
9
10
  */
10
- export declare function isFunctionHigherOrder(id: NodeId, graph: DataflowGraph, ctx: ReadOnlyFlowrAnalyzerContext): boolean;
11
+ export declare function isFunctionHigherOrder(id: NodeId, graph: DataflowGraph, ctx: ReadOnlyFlowrAnalyzerContext, invertedGraph?: DataflowGraph): boolean;
@@ -32,8 +32,8 @@ function isAnyReturnAFunction(def, graph) {
32
32
  }
33
33
  return false;
34
34
  }
35
- function inspectCallSitesArgumentsFns(def, graph, ctx) {
36
- const callSites = graph.ingoingEdges(def.id);
35
+ function inspectCallSitesArgumentsFns(def, graph, ctx, invertedGraph) {
36
+ const callSites = invertedGraph?.outgoingEdges(def.id) ?? graph.ingoingEdges(def.id);
37
37
  for (const [callerId, e] of callSites ?? []) {
38
38
  if (!edge_1.DfEdge.includesType(e, edge_1.EdgeType.Calls)) {
39
39
  continue;
@@ -59,8 +59,9 @@ function inspectCallSitesArgumentsFns(def, graph, ctx) {
59
59
  * either takes a function as an argument or (may) returns a function.
60
60
  * If the return is an identity, e.g., `function(x) x`, this is not considered higher-order,
61
61
  * if no function is passed as an argument.
62
+ * Please note that inspecting higher order functions can be sped up (if queries multiple times) by providing an inverted graph as well!
62
63
  */
63
- function isFunctionHigherOrder(id, graph, ctx) {
64
+ function isFunctionHigherOrder(id, graph, ctx, invertedGraph) {
64
65
  const vert = graph.getVertex(id);
65
66
  if (!vert || !(0, vertex_1.isFunctionDefinitionVertex)(vert)) {
66
67
  return false;
@@ -70,6 +71,6 @@ function isFunctionHigherOrder(id, graph, ctx) {
70
71
  return true;
71
72
  }
72
73
  // 2. check whether any of the callsites passes a function
73
- return inspectCallSitesArgumentsFns(vert, graph, ctx);
74
+ return inspectCallSitesArgumentsFns(vert, graph, ctx, invertedGraph);
74
75
  }
75
76
  //# sourceMappingURL=higher-order-function.js.map
@@ -169,8 +169,9 @@ class DataflowGraph {
169
169
  ingoingEdges(id) {
170
170
  const edges = new Map();
171
171
  for (const [source, outgoing] of this.edgeInformation.entries()) {
172
- if (outgoing.has(id)) {
173
- edges.set(source, outgoing.get(id));
172
+ const o = outgoing.get(id);
173
+ if (o) {
174
+ edges.set(source, o);
174
175
  }
175
176
  }
176
177
  return edges;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.9.3",
3
+ "version": "2.9.5",
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": {
@@ -4,6 +4,7 @@ exports.executeHigherOrderQuery = executeHigherOrderQuery;
4
4
  const higher_order_function_1 = require("../../../dataflow/fn/higher-order-function");
5
5
  const parse_1 = require("../../../slicing/criterion/parse");
6
6
  const vertex_1 = require("../../../dataflow/graph/vertex");
7
+ const invert_dfg_1 = require("../../../dataflow/graph/invert-dfg");
7
8
  /**
8
9
  * Execute higher-order function inspection queries on the given analyzer.
9
10
  */
@@ -34,9 +35,13 @@ async function executeHigherOrderQuery({ analyzer }, queries) {
34
35
  const graph = (await analyzer.dataflow()).graph;
35
36
  const fns = graph.verticesOfType(vertex_1.VertexType.FunctionDefinition)
36
37
  .filter(([, v]) => filterFor.size === 0 || filterFor.has(v.id));
38
+ let invertedGraph;
39
+ if (filterFor.size === 0 || filterFor.size > 10) {
40
+ invertedGraph = (0, invert_dfg_1.invertDfg)(graph, analyzer.inspectContext().env.makeCleanEnv());
41
+ }
37
42
  const result = {};
38
43
  for (const [id] of fns) {
39
- result[id] = (0, higher_order_function_1.isFunctionHigherOrder)(id, graph, analyzer.inspectContext());
44
+ result[id] = (0, higher_order_function_1.isFunctionHigherOrder)(id, graph, analyzer.inspectContext(), invertedGraph);
40
45
  }
41
46
  return {
42
47
  '.meta': {
package/util/version.js CHANGED
@@ -6,7 +6,7 @@ exports.printVersionInformation = printVersionInformation;
6
6
  const semver_1 = require("semver");
7
7
  const assert_1 = require("./assert");
8
8
  // this is automatically replaced with the current version by release-it
9
- const version = '2.9.3';
9
+ const version = '2.9.5';
10
10
  /**
11
11
  * Retrieves the current flowR version as a new {@link SemVer} object.
12
12
  */