@eagleoutice/flowr 2.7.6 → 2.8.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 (208) hide show
  1. package/README.md +67 -64
  2. package/cli/wiki.js +1 -1
  3. package/control-flow/extract-cfg.js +3 -3
  4. package/control-flow/useless-loop.d.ts +1 -1
  5. package/control-flow/useless-loop.js +2 -2
  6. package/dataflow/cluster.js +3 -3
  7. package/dataflow/environments/built-in-config.d.ts +8 -4
  8. package/dataflow/environments/built-in.d.ts +27 -14
  9. package/dataflow/environments/built-in.js +27 -12
  10. package/dataflow/environments/default-builtin-config.d.ts +615 -3
  11. package/dataflow/environments/default-builtin-config.js +50 -15
  12. package/dataflow/environments/environment.js +3 -2
  13. package/dataflow/environments/identifier.d.ts +5 -1
  14. package/dataflow/environments/reference-to-maybe.d.ts +2 -2
  15. package/dataflow/environments/reference-to-maybe.js +23 -14
  16. package/dataflow/environments/resolve-by-name.d.ts +6 -2
  17. package/dataflow/environments/resolve-by-name.js +5 -1
  18. package/dataflow/environments/scoping.js +1 -3
  19. package/dataflow/eval/resolve/alias-tracking.js +5 -1
  20. package/dataflow/extractor.js +3 -3
  21. package/dataflow/fn/exceptions-of-function.d.ts +13 -0
  22. package/dataflow/fn/exceptions-of-function.js +47 -0
  23. package/dataflow/fn/higher-order-function.d.ts +1 -1
  24. package/dataflow/fn/higher-order-function.js +3 -3
  25. package/dataflow/fn/recursive-function.d.ts +6 -0
  26. package/dataflow/fn/recursive-function.js +32 -0
  27. package/dataflow/graph/call-graph.d.ts +10 -0
  28. package/dataflow/graph/call-graph.js +212 -0
  29. package/dataflow/graph/dataflowgraph-builder.d.ts +7 -2
  30. package/dataflow/graph/dataflowgraph-builder.js +14 -9
  31. package/dataflow/graph/diff-dataflow-graph.js +96 -2
  32. package/dataflow/graph/graph.d.ts +10 -7
  33. package/dataflow/graph/graph.js +7 -8
  34. package/dataflow/graph/vertex.d.ts +6 -3
  35. package/dataflow/hooks.d.ts +30 -0
  36. package/dataflow/hooks.js +38 -0
  37. package/dataflow/info.d.ts +28 -5
  38. package/dataflow/info.js +66 -31
  39. package/dataflow/internal/linker.d.ts +13 -3
  40. package/dataflow/internal/linker.js +163 -73
  41. package/dataflow/internal/process/functions/call/argument/unpack-argument.d.ts +4 -0
  42. package/dataflow/internal/process/functions/call/argument/unpack-argument.js +7 -0
  43. package/dataflow/internal/process/functions/call/built-in/built-in-apply.d.ts +1 -1
  44. package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +19 -3
  45. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +14 -0
  46. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +30 -0
  47. package/dataflow/internal/process/functions/call/built-in/built-in-eval.js +2 -1
  48. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +24 -17
  49. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +2 -1
  50. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.d.ts +5 -1
  51. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +59 -21
  52. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +4 -3
  53. package/dataflow/internal/process/functions/call/built-in/built-in-register-hook.d.ts +34 -0
  54. package/dataflow/internal/process/functions/call/built-in/built-in-register-hook.js +92 -0
  55. package/dataflow/internal/process/functions/call/built-in/built-in-repeat-loop.js +1 -0
  56. package/dataflow/internal/process/functions/call/built-in/built-in-stop-if-not.d.ts +21 -0
  57. package/dataflow/internal/process/functions/call/built-in/built-in-stop-if-not.js +129 -0
  58. package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.d.ts +16 -0
  59. package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.js +127 -0
  60. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +5 -3
  61. package/dataflow/internal/process/functions/call/common.d.ts +13 -1
  62. package/dataflow/internal/process/functions/call/common.js +33 -2
  63. package/dataflow/internal/process/functions/call/known-call-handling.d.ts +13 -1
  64. package/dataflow/internal/process/functions/call/known-call-handling.js +29 -3
  65. package/dataflow/internal/process/functions/call/named-call-handling.js +2 -1
  66. package/dataflow/internal/process/functions/call/unnamed-call-handling.js +6 -4
  67. package/dataflow/internal/process/functions/process-argument.js +7 -6
  68. package/dataflow/internal/process/functions/process-parameter.js +2 -1
  69. package/dataflow/internal/process/process-named-call.d.ts +2 -2
  70. package/dataflow/internal/process/process-symbol.js +3 -2
  71. package/dataflow/internal/process/process-value.d.ts +3 -2
  72. package/dataflow/internal/process/process-value.js +8 -6
  73. package/dataflow/origin/dfg-get-origin.js +2 -1
  74. package/dataflow/origin/dfg-get-symbol-refs.js +1 -1
  75. package/documentation/doc-readme.d.ts +1 -1
  76. package/documentation/doc-readme.js +6 -6
  77. package/documentation/doc-util/doc-code.js +1 -1
  78. package/documentation/doc-util/doc-dfg.d.ts +1 -0
  79. package/documentation/doc-util/doc-dfg.js +7 -4
  80. package/documentation/doc-util/doc-query.d.ts +1 -0
  81. package/documentation/doc-util/doc-query.js +1 -1
  82. package/documentation/doc-util/doc-repl.d.ts +2 -1
  83. package/documentation/doc-util/doc-repl.js +11 -3
  84. package/documentation/wiki-analyzer.js +2 -0
  85. package/documentation/wiki-dataflow-graph.js +59 -16
  86. package/documentation/wiki-interface.js +33 -5
  87. package/documentation/wiki-mk/doc-context.d.ts +2 -1
  88. package/documentation/wiki-mk/doc-context.js +2 -2
  89. package/documentation/wiki-mk/doc-maker.js +4 -3
  90. package/documentation/wiki-normalized-ast.js +6 -0
  91. package/documentation/wiki-query.js +109 -1
  92. package/linter/linter-rules.d.ts +1 -1
  93. package/linter/rules/seeded-randomness.js +17 -12
  94. package/linter/rules/useless-loop.d.ts +1 -1
  95. package/package.json +9 -9
  96. package/project/cache/flowr-analyzer-cache.d.ts +11 -0
  97. package/project/cache/flowr-analyzer-cache.js +19 -0
  98. package/project/context/flowr-analyzer-dependencies-context.d.ts +6 -1
  99. package/project/context/flowr-analyzer-dependencies-context.js +6 -0
  100. package/project/context/flowr-analyzer-files-context.d.ts +5 -2
  101. package/project/context/flowr-analyzer-files-context.js +24 -17
  102. package/project/context/flowr-file.d.ts +9 -4
  103. package/project/context/flowr-file.js +20 -6
  104. package/project/flowr-analyzer.d.ts +11 -0
  105. package/project/flowr-analyzer.js +6 -0
  106. package/project/plugins/file-plugins/files/flowr-description-file.d.ts +8 -0
  107. package/project/plugins/file-plugins/files/flowr-description-file.js +36 -3
  108. package/project/plugins/file-plugins/files/flowr-jupyter-file.js +1 -1
  109. package/project/plugins/file-plugins/files/flowr-namespace-file.js +1 -1
  110. package/project/plugins/file-plugins/files/flowr-news-file.js +1 -1
  111. package/project/plugins/file-plugins/files/flowr-rmarkdown-file.js +1 -1
  112. package/project/plugins/file-plugins/flowr-analyzer-description-file-plugin.js +1 -1
  113. package/project/plugins/file-plugins/flowr-analyzer-file-plugin.d.ts +4 -1
  114. package/project/plugins/file-plugins/flowr-analyzer-file-plugin.js +3 -0
  115. package/project/plugins/file-plugins/{flowr-analyzer-namespace-file-plugin.d.ts → flowr-analyzer-namespace-files-plugin.d.ts} +1 -1
  116. package/project/plugins/file-plugins/{flowr-analyzer-namespace-file-plugin.js → flowr-analyzer-namespace-files-plugin.js} +4 -4
  117. package/project/plugins/file-plugins/flowr-analyzer-test-file-plugin.d.ts +26 -0
  118. package/project/plugins/file-plugins/flowr-analyzer-test-file-plugin.js +39 -0
  119. package/project/plugins/file-plugins/flowr-analyzer-vignette-file-plugin.d.ts +26 -0
  120. package/project/plugins/file-plugins/flowr-analyzer-vignette-file-plugin.js +39 -0
  121. package/project/plugins/flowr-analyzer-plugin-defaults.js +6 -2
  122. package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-description-file-plugin.js +3 -13
  123. package/project/plugins/package-version-plugins/package.d.ts +1 -1
  124. package/project/plugins/package-version-plugins/package.js +3 -3
  125. package/project/plugins/plugin-registry.d.ts +4 -2
  126. package/project/plugins/plugin-registry.js +6 -2
  127. package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.d.ts +11 -0
  128. package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +5 -2
  129. package/queries/catalog/call-context-query/call-context-query-format.d.ts +4 -12
  130. package/queries/catalog/call-graph-query/call-graph-query-executor.d.ts +6 -0
  131. package/queries/catalog/call-graph-query/call-graph-query-executor.js +21 -0
  132. package/queries/catalog/call-graph-query/call-graph-query-format.d.ts +21 -0
  133. package/queries/catalog/call-graph-query/call-graph-query-format.js +32 -0
  134. package/queries/catalog/dataflow-query/dataflow-query-executor.js +4 -3
  135. package/queries/catalog/dependencies-query/dependencies-query-executor.js +29 -3
  136. package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +1 -0
  137. package/queries/catalog/dependencies-query/function-info/function-info.d.ts +8 -1
  138. package/queries/catalog/dependencies-query/function-info/write-functions.js +13 -0
  139. package/queries/catalog/does-call-query/does-call-query-executor.d.ts +6 -0
  140. package/queries/catalog/does-call-query/does-call-query-executor.js +100 -0
  141. package/queries/catalog/does-call-query/does-call-query-format.d.ts +51 -0
  142. package/queries/catalog/does-call-query/does-call-query-format.js +102 -0
  143. package/queries/catalog/files-query/files-query-executor.js +4 -4
  144. package/queries/catalog/files-query/files-query-format.d.ts +2 -1
  145. package/queries/catalog/files-query/files-query-format.js +18 -2
  146. package/queries/catalog/id-map-query/id-map-query-executor.js +4 -3
  147. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-executor.d.ts +18 -0
  148. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-executor.js +56 -0
  149. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-format.d.ts +34 -0
  150. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-format.js +54 -0
  151. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.js +3 -3
  152. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.d.ts +6 -0
  153. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.js +12 -0
  154. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-executor.d.ts +6 -0
  155. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-executor.js +23 -0
  156. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-format.d.ts +28 -0
  157. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-format.js +44 -0
  158. package/queries/catalog/linter-query/linter-query-format.js +4 -1
  159. package/queries/catalog/location-map-query/location-map-query-executor.js +1 -1
  160. package/queries/catalog/normalized-ast-query/normalized-ast-query-executor.js +4 -3
  161. package/queries/catalog/project-query/project-query-executor.js +9 -3
  162. package/queries/catalog/project-query/project-query-format.d.ts +6 -1
  163. package/queries/catalog/project-query/project-query-format.js +35 -9
  164. package/queries/query.d.ts +34 -2
  165. package/queries/query.js +9 -0
  166. package/r-bridge/data/data.d.ts +10 -5
  167. package/r-bridge/data/data.js +11 -5
  168. package/r-bridge/lang-4.x/ast/model/model.d.ts +7 -7
  169. package/r-bridge/lang-4.x/ast/model/nodes/r-access.d.ts +2 -2
  170. package/r-bridge/lang-4.x/ast/model/nodes/r-argument.d.ts +2 -2
  171. package/r-bridge/lang-4.x/ast/model/nodes/r-binary-op.d.ts +2 -2
  172. package/r-bridge/lang-4.x/ast/model/nodes/r-comment.d.ts +5 -2
  173. package/r-bridge/lang-4.x/ast/model/nodes/r-comment.js +8 -0
  174. package/r-bridge/lang-4.x/ast/model/nodes/r-expression-list.d.ts +2 -2
  175. package/r-bridge/lang-4.x/ast/model/nodes/r-for-loop.d.ts +2 -2
  176. package/r-bridge/lang-4.x/ast/model/nodes/r-function-call.d.ts +3 -3
  177. package/r-bridge/lang-4.x/ast/model/nodes/r-function-definition.d.ts +2 -2
  178. package/r-bridge/lang-4.x/ast/model/nodes/r-if-then-else.d.ts +2 -2
  179. package/r-bridge/lang-4.x/ast/model/nodes/r-parameter.d.ts +2 -2
  180. package/r-bridge/lang-4.x/ast/model/nodes/r-pipe.d.ts +2 -2
  181. package/r-bridge/lang-4.x/ast/model/nodes/r-repeat-loop.d.ts +2 -2
  182. package/r-bridge/lang-4.x/ast/model/nodes/r-unary-op.d.ts +2 -2
  183. package/r-bridge/lang-4.x/ast/model/nodes/r-while-loop.d.ts +2 -2
  184. package/r-bridge/lang-4.x/ast/parser/main/internal/other/normalize-comment.js +0 -1
  185. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +0 -2
  186. package/r-bridge/roxygen2/roxygen-ast.d.ts +218 -0
  187. package/r-bridge/roxygen2/roxygen-ast.js +82 -0
  188. package/r-bridge/roxygen2/roxygen-parse.d.ts +24 -0
  189. package/r-bridge/roxygen2/roxygen-parse.js +214 -0
  190. package/reconstruct/auto-select/magic-comments.js +4 -4
  191. package/slicing/static/slice-call.js +3 -4
  192. package/slicing/static/static-slicer.js +2 -2
  193. package/statistics/features/supported/defined-functions/defined-functions.js +1 -1
  194. package/util/collections/defaultmap.d.ts +3 -3
  195. package/util/mermaid/dfg.js +5 -5
  196. package/util/objects.js +1 -1
  197. package/util/r-author.d.ts +5 -0
  198. package/util/r-author.js +110 -0
  199. package/util/r-license.d.ts +10 -1
  200. package/util/r-license.js +27 -6
  201. package/util/r-version.d.ts +19 -0
  202. package/util/r-version.js +106 -0
  203. package/util/range.d.ts +6 -0
  204. package/util/range.js +7 -0
  205. package/util/simple-df/dfg-ascii.js +2 -2
  206. package/util/text/args.d.ts +9 -0
  207. package/util/text/args.js +65 -0
  208. package/util/version.js +1 -1
@@ -4,11 +4,13 @@ import type { IdentifierReference } from './environments/identifier';
4
4
  import type { REnvironmentInformation } from './environments/environment';
5
5
  import { DataflowGraph } from './graph/graph';
6
6
  import type { GenericDifferenceInformation, WriteableDifferenceReport } from '../util/diff';
7
+ import type { HookInformation } from './hooks';
7
8
  /**
8
9
  * A control dependency links a vertex to the control flow element which
9
10
  * may have an influence on its execution.
10
11
  * Within `if(p) a else b`, `a` and `b` have a control dependency on the `if` (which in turn decides based on `p`).
11
12
  * @see {@link happensInEveryBranch} - to check whether a list of control dependencies is exhaustive
13
+ * @see {@link negateControlDependency} - to easily negate a control dependency
12
14
  */
13
15
  export interface ControlDependency {
14
16
  /** The id of the node that causes the control dependency to be active (e.g., the condition of an if) */
@@ -18,6 +20,11 @@ export interface ControlDependency {
18
20
  /** whether this control dependency was created due to iteration (e.g., a loop) */
19
21
  readonly byIteration?: boolean;
20
22
  }
23
+ /**
24
+ * Negates the given control dependency (i.e., flips the `when` flag).
25
+ * This keeps undefined `when` values intact as undefined.
26
+ */
27
+ export declare function negateControlDependency(cd: ControlDependency): ControlDependency;
21
28
  /**
22
29
  * Classifies the type of exit point encountered.
23
30
  * @see {@link ExitPoint}
@@ -30,8 +37,14 @@ export declare const enum ExitPointType {
30
37
  /** The exit point is an explicit `break` call (or an alias of it) */
31
38
  Break = 2,
32
39
  /** The exit point is an explicit `next` call (or an alias of it) */
33
- Next = 3
40
+ Next = 3,
41
+ /** The exit point is caused by an error being thrown, e.g., by `stop` or `stopifnot` */
42
+ Error = 4
34
43
  }
44
+ /**
45
+ * Checks whether the given exit point type propagates calls (i.e., whether it aborts the current function execution).
46
+ */
47
+ export declare function doesExitPointPropagateCalls(type: ExitPointType): boolean;
35
48
  /**
36
49
  * An exit point describes the position which ends the current control flow structure.
37
50
  * This may be as innocent as the last expression or explicit with a `return`/`break`/`next`.
@@ -50,18 +63,28 @@ export interface ExitPoint {
50
63
  * (e.g., if the `return` is contained within an `if` statement).
51
64
  * @see {@link happensInEveryBranch} - to check whether control dependencies are exhaustive
52
65
  */
53
- readonly controlDependencies: ControlDependency[] | undefined;
66
+ readonly controlDependencies?: ControlDependency[] | undefined;
54
67
  }
55
68
  /**
56
- * Adds all non-default exit points to the existing list.
69
+ * Adds all non-default exit points to the existing list and updates the `invertExitCds` accordingly.
57
70
  */
58
- export declare function addNonDefaultExitPoints(existing: ExitPoint[], add: readonly ExitPoint[]): void;
71
+ export declare function addNonDefaultExitPoints(existing: ExitPoint[], invertExitCds: ControlDependency[], activeCds: ControlDependency[] | undefined, add: readonly ExitPoint[]): void;
72
+ /**
73
+ * Overwrites the existing exit points with the given ones, taking care of cds.
74
+ */
75
+ export declare function overwriteExitPoints(existing: readonly ExitPoint[], replace: ExitPoint[]): ExitPoint[];
59
76
  /** The control flow information for the current DataflowInformation. */
60
77
  export interface DataflowCfgInformation {
61
78
  /** The entry node into the subgraph */
62
79
  entryPoint: NodeId;
63
- /** All already identified exit points (active 'return'/'break'/'next'-likes) of the respective structure. */
80
+ /**
81
+ * All already identified exit points (active 'return'/'break'/'next'-likes) of the respective structure.
82
+ * This also tracks (local knowledge of) exceptions thrown within the structure.
83
+ * See the {@link ExitPointType#Error|Error} type for more information.
84
+ */
64
85
  exitPoints: readonly ExitPoint[];
86
+ /** Registered hooks within the current subtree */
87
+ hooks: HookInformation[];
65
88
  }
66
89
  /**
67
90
  * The dataflow information is one of the fundamental structures we have in the dataflow analysis.
package/dataflow/info.js CHANGED
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.negateControlDependency = negateControlDependency;
4
+ exports.doesExitPointPropagateCalls = doesExitPointPropagateCalls;
3
5
  exports.addNonDefaultExitPoints = addNonDefaultExitPoints;
6
+ exports.overwriteExitPoints = overwriteExitPoints;
4
7
  exports.initializeCleanDataflowInformation = initializeCleanDataflowInformation;
5
8
  exports.happensInEveryBranch = happensInEveryBranch;
6
9
  exports.happensInEveryBranchSet = happensInEveryBranchSet;
@@ -9,11 +12,48 @@ exports.filterOutLoopExitPoints = filterOutLoopExitPoints;
9
12
  exports.diffControlDependency = diffControlDependency;
10
13
  exports.diffControlDependencies = diffControlDependencies;
11
14
  const graph_1 = require("./graph/graph");
15
+ const assert_1 = require("../util/assert");
12
16
  /**
13
- * Adds all non-default exit points to the existing list.
17
+ * Negates the given control dependency (i.e., flips the `when` flag).
18
+ * This keeps undefined `when` values intact as undefined.
14
19
  */
15
- function addNonDefaultExitPoints(existing, add) {
16
- existing.push(...add.filter(({ type }) => type !== 0 /* ExitPointType.Default */));
20
+ function negateControlDependency(cd) {
21
+ return {
22
+ ...cd,
23
+ when: cd.when === undefined ? undefined : !cd.when,
24
+ };
25
+ }
26
+ /**
27
+ * Checks whether the given exit point type propagates calls (i.e., whether it aborts the current function execution).
28
+ */
29
+ function doesExitPointPropagateCalls(type) {
30
+ return type === 4 /* ExitPointType.Error */;
31
+ }
32
+ /**
33
+ * Adds all non-default exit points to the existing list and updates the `invertExitCds` accordingly.
34
+ */
35
+ function addNonDefaultExitPoints(existing, invertExitCds, activeCds, add) {
36
+ const toAdd = add.filter(({ type }) => type !== 0 /* ExitPointType.Default */);
37
+ if (toAdd.length === 0) {
38
+ return;
39
+ }
40
+ const invertedCds = toAdd.flatMap(e => e.controlDependencies?.filter(icd => !activeCds?.some(e => e.id === icd.id && e.when === icd.when)).map(negateControlDependency)).filter(assert_1.isNotUndefined);
41
+ existing.push(...toAdd);
42
+ for (const icd of invertedCds) {
43
+ if (!invertExitCds.some(e => e.id === icd.id && e.when === icd.when)) {
44
+ invertExitCds.push(icd);
45
+ }
46
+ }
47
+ }
48
+ /**
49
+ * Overwrites the existing exit points with the given ones, taking care of cds.
50
+ */
51
+ function overwriteExitPoints(existing, replace) {
52
+ const replaceCds = replace.flatMap(e => e.controlDependencies);
53
+ if (replaceCds.length === 0 || replaceCds.some(r => r === undefined) || happensInEveryBranch(replaceCds.filter(e => e !== undefined))) {
54
+ return replace;
55
+ }
56
+ return existing.concat(replace);
17
57
  }
18
58
  /**
19
59
  * Initializes an empty {@link DataflowInformation} object with the given entry point and data.
@@ -28,7 +68,8 @@ function initializeCleanDataflowInformation(entryPoint, data) {
28
68
  environment: data.environment,
29
69
  graph: new graph_1.DataflowGraph(data.completeAst.idMap),
30
70
  entryPoint,
31
- exitPoints: [{ nodeId: entryPoint, type: 0 /* ExitPointType.Default */, controlDependencies: undefined }]
71
+ exitPoints: [{ nodeId: entryPoint, type: 0 /* ExitPointType.Default */ }],
72
+ hooks: []
32
73
  };
33
74
  }
34
75
  /**
@@ -37,28 +78,21 @@ function initializeCleanDataflowInformation(entryPoint, data) {
37
78
  * @see {@link happensInEveryBranchSet} - for the set-based version
38
79
  */
39
80
  function happensInEveryBranch(controlDependencies) {
40
- if (controlDependencies === undefined) {
41
- /* the cds are unconstrained */
42
- return true;
43
- }
44
- else if (controlDependencies.length === 0) {
45
- /* this happens only when we have no idea and require more analysis */
46
- return false;
47
- }
48
- return coversSet(controlDependencies);
81
+ /* this happens only when we have no idea and require more analysis */
82
+ return controlDependencies === undefined || (controlDependencies.length !== 0 && coversSet(controlDependencies));
49
83
  }
50
84
  function coversSet(controlDependencies) {
51
- const trues = [];
52
- const falseSet = new Set();
85
+ const trues = new Set();
86
+ const falses = new Set();
53
87
  for (const { id, when } of controlDependencies) {
54
88
  if (when) {
55
- trues.push(id);
89
+ trues.add(id);
56
90
  }
57
- else {
58
- falseSet.add(id);
91
+ else if (when === false) {
92
+ falses.add(id);
59
93
  }
60
94
  }
61
- return trues.every(id => falseSet.has(id));
95
+ return trues.symmetricDifference(falses).size === 0;
62
96
  }
63
97
  /**
64
98
  * Checks whether the given control dependencies are exhaustive (i.e. if for every control dependency on a boolean,
@@ -66,28 +100,29 @@ function coversSet(controlDependencies) {
66
100
  * @see {@link happensInEveryBranch} - for the array-based version
67
101
  */
68
102
  function happensInEveryBranchSet(controlDependencies) {
69
- if (controlDependencies === undefined) {
70
- /* the cds are unconstrained */
71
- return true;
72
- }
73
- else if (controlDependencies.size === 0) {
74
- /* this happens only when we have no idea and require more analysis */
75
- return false;
76
- }
77
- return coversSet(controlDependencies);
103
+ return controlDependencies === undefined || (controlDependencies.size !== 0 && coversSet(controlDependencies));
78
104
  }
79
105
  /**
80
106
  * Checks whether the given dataflow information always exits (i.e., if there is a non-default exit point in every branch).
81
107
  * @see {@link ExitPoint} - for the different types of exit points
82
108
  */
83
109
  function alwaysExits(data) {
84
- return data.exitPoints?.some(e => e.type !== 0 /* ExitPointType.Default */ && happensInEveryBranch(e.controlDependencies)) ?? false;
110
+ let cds = [];
111
+ for (const e of data.exitPoints) {
112
+ if (e.type !== 0 /* ExitPointType.Default */) {
113
+ if (e.controlDependencies === undefined) {
114
+ return true;
115
+ }
116
+ cds = cds.concat(e.controlDependencies);
117
+ }
118
+ }
119
+ return happensInEveryBranch(cds);
85
120
  }
86
121
  /**
87
122
  * Filters out exit points which end their cascade within a loop.
88
123
  */
89
124
  function filterOutLoopExitPoints(exitPoints) {
90
- return exitPoints.filter(({ type }) => type === 1 /* ExitPointType.Return */ || type === 0 /* ExitPointType.Default */);
125
+ return exitPoints.filter(({ type }) => type !== 2 /* ExitPointType.Break */ && type !== 3 /* ExitPointType.Next */);
91
126
  }
92
127
  /**
93
128
  * Calculates the difference between two control dependencies.
@@ -100,7 +135,7 @@ function diffControlDependency(a, b, info) {
100
135
  return;
101
136
  }
102
137
  if (a.id !== b.id) {
103
- info.report.addComment(`${info.position}Different control dependency ids. ${info.leftname}: ${a.id} vs. ${info.rightname}: ${b.id}`);
138
+ info.report.addComment(`${info.position}Different control dependency ids. ${info.leftname}: ${JSON.stringify(a.id)} vs. ${info.rightname}: ${JSON.stringify(b.id)}`);
104
139
  }
105
140
  if (a.when !== b.when) {
106
141
  info.report.addComment(`${info.position}Different control dependency when. ${info.leftname}: ${a.when} vs. ${info.rightname}: ${b.when}`);
@@ -21,12 +21,21 @@ export declare function produceNameSharedIdMap(references: IdentifierReference[]
21
21
  * Links the given arguments to the given parameters within the given graph.
22
22
  * This follows the `pmatch` semantics of R
23
23
  * @see https://cran.r-project.org/doc/manuals/R-lang.html#Argument-matching
24
+ * This returns the resolved map from argument ids to parameter ids.
25
+ * If you just want to match by name, use {@link pMatch}.
24
26
  */
25
- export declare function linkArgumentsOnCall(args: FunctionArgument[], params: RParameter<ParentInformation>[], graph: DataflowGraph): void;
27
+ export declare function linkArgumentsOnCall(args: readonly FunctionArgument[], params: readonly RParameter<ParentInformation>[], graph: DataflowGraph): Map<NodeId, NodeId>;
28
+ /**
29
+ * Links the given arguments to the given parameters within the given graph by name only.
30
+ * @note
31
+ * To obtain the arguments from a {@link RFunctionCall}[], either use {@link processAllArguments} (also available via {@link processKnownFunctionCall})
32
+ * or convert them with {@link convertFnArguments}.
33
+ */
34
+ export declare function pMatch<Targets extends NodeId>(args: readonly FunctionArgument[], params: Record<string, Targets>): Map<NodeId, Targets>;
26
35
  /**
27
36
  * Links a function call with a single target function definition.
28
37
  */
29
- export declare function linkFunctionCallWithSingleTarget(graph: DataflowGraph, def: DataflowGraphVertexFunctionDefinition, info: DataflowGraphVertexFunctionCall, idMap: AstIdMap): void;
38
+ export declare function linkFunctionCallWithSingleTarget(graph: DataflowGraph, { subflow: fnSubflow, exitPoints, id: fnId, params }: DataflowGraphVertexFunctionDefinition, info: DataflowGraphVertexFunctionCall, idMap: AstIdMap): ExitPoint[];
30
39
  /**
31
40
  * Returns the called functions within the current graph, which can be used to merge the environments with the call.
32
41
  * Furthermore, it links the corresponding arguments.
@@ -37,6 +46,7 @@ export declare function linkFunctionCallWithSingleTarget(graph: DataflowGraph, d
37
46
  export declare function linkFunctionCalls(graph: DataflowGraph, idMap: AstIdMap, thisGraph: DataflowGraph): {
38
47
  functionCall: NodeId;
39
48
  called: readonly DataflowGraphVertexInfo[];
49
+ propagateExitPoints: readonly ExitPoint[];
40
50
  }[];
41
51
  /**
42
52
  * convenience function returning all known call targets, as well as the name source which defines them
@@ -45,7 +55,7 @@ export declare function getAllFunctionCallTargets(call: NodeId, graph: DataflowG
45
55
  /**
46
56
  * Finds all linked function definitions starting from the given set of read ids.
47
57
  */
48
- export declare function getAllLinkedFunctionDefinitions(functionDefinitionReadIds: ReadonlySet<NodeId>, dataflowGraph: DataflowGraph): [Set<DataflowGraphVertexInfo>, Set<BuiltIn>];
58
+ export declare function getAllLinkedFunctionDefinitions(functionDefinitionReadIds: ReadonlySet<NodeId>, dataflowGraph: DataflowGraph): [Set<Required<DataflowGraphVertexFunctionDefinition>>, Set<BuiltIn>];
49
59
  /**
50
60
  * This method links a set of read variables to definitions in an environment.
51
61
  * @param referencesToLinkAgainstEnvironment - The set of references to link against the environment
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findNonLocalReads = findNonLocalReads;
4
4
  exports.produceNameSharedIdMap = produceNameSharedIdMap;
5
5
  exports.linkArgumentsOnCall = linkArgumentsOnCall;
6
+ exports.pMatch = pMatch;
6
7
  exports.linkFunctionCallWithSingleTarget = linkFunctionCallWithSingleTarget;
7
8
  exports.linkFunctionCalls = linkFunctionCalls;
8
9
  exports.getAllFunctionCallTargets = getAllFunctionCallTargets;
@@ -24,6 +25,7 @@ const vertex_1 = require("../graph/vertex");
24
25
  const resolve_by_name_1 = require("../environments/resolve-by-name");
25
26
  const built_in_1 = require("../environments/built-in");
26
27
  const prefix_1 = require("../../util/prefix");
28
+ const info_1 = require("../info");
27
29
  /**
28
30
  * Find all reads within the graph that do not reference a local definition in the graph.
29
31
  */
@@ -32,33 +34,21 @@ function findNonLocalReads(graph, ignore) {
32
34
  const ids = new Set(graph.vertexIdsOfType(vertex_1.VertexType.Use).concat(graph.vertexIdsOfType(vertex_1.VertexType.FunctionCall)));
33
35
  /* find all variable use ids which do not link to a given id */
34
36
  const nonLocalReads = [];
35
- for (const id of ids) {
36
- if (ignores.has(id)) {
37
+ for (const nodeId of ids) {
38
+ if (ignores.has(nodeId)) {
37
39
  continue;
38
40
  }
39
- const outgoing = graph.outgoingEdges(id);
40
- const name = (0, node_id_1.recoverName)(id, graph.idMap);
41
- const origin = graph.getVertex(id, true);
41
+ const outgoing = graph.outgoingEdges(nodeId);
42
+ const name = (0, node_id_1.recoverName)(nodeId, graph.idMap);
43
+ const origin = graph.getVertex(nodeId, true);
44
+ const type = origin?.tag === vertex_1.VertexType.FunctionCall ? identifier_1.ReferenceType.Function : identifier_1.ReferenceType.Variable;
42
45
  if (outgoing === undefined) {
43
- nonLocalReads.push({
44
- name: (0, node_id_1.recoverName)(id, graph.idMap),
45
- nodeId: id,
46
- controlDependencies: undefined,
47
- type: origin?.tag === vertex_1.VertexType.FunctionCall ? identifier_1.ReferenceType.Function : identifier_1.ReferenceType.Variable
48
- });
46
+ nonLocalReads.push({ name, nodeId, type });
49
47
  continue;
50
48
  }
51
49
  for (const [target, { types }] of outgoing) {
52
50
  if ((0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.Reads) && !ids.has(target)) {
53
- if (!name) {
54
- logger_1.dataflowLogger.warn('found non-local read without name for id ' + id);
55
- }
56
- nonLocalReads.push({
57
- name: (0, node_id_1.recoverName)(id, graph.idMap),
58
- nodeId: id,
59
- controlDependencies: undefined,
60
- type: origin?.tag === vertex_1.VertexType.FunctionCall ? identifier_1.ReferenceType.Function : identifier_1.ReferenceType.Variable
61
- });
51
+ nonLocalReads.push({ name, nodeId, type });
62
52
  break;
63
53
  }
64
54
  }
@@ -81,26 +71,34 @@ function produceNameSharedIdMap(references) {
81
71
  * Links the given arguments to the given parameters within the given graph.
82
72
  * This follows the `pmatch` semantics of R
83
73
  * @see https://cran.r-project.org/doc/manuals/R-lang.html#Argument-matching
74
+ * This returns the resolved map from argument ids to parameter ids.
75
+ * If you just want to match by name, use {@link pMatch}.
84
76
  */
85
77
  function linkArgumentsOnCall(args, params, graph) {
86
78
  const nameArgMap = new Map(args.filter(graph_1.isNamedArgument).map(a => [a.name, a]));
87
79
  const nameParamMap = new Map(params.filter(p => p?.name?.content !== undefined)
88
80
  .map(p => [p.name.content, p]));
81
+ const maps = new Map();
89
82
  const specialDotParameter = params.find(p => p.special);
83
+ const sid = specialDotParameter?.name.info.id;
90
84
  // all parameters matched by name
91
85
  const matchedParameters = new Set();
86
+ const paramNames = nameParamMap.keys().toArray();
92
87
  // first map names
93
88
  for (const [name, { nodeId: argId }] of nameArgMap) {
94
- const pmatchName = (0, prefix_1.findByPrefixIfUnique)(name, nameParamMap.keys()) ?? name;
89
+ const pmatchName = (0, prefix_1.findByPrefixIfUnique)(name, paramNames) ?? name;
95
90
  const param = nameParamMap.get(pmatchName);
96
91
  if (param?.name) {
97
- graph.addEdge(argId, param.name.info.id, edge_1.EdgeType.DefinesOnCall);
98
- graph.addEdge(param.name.info.id, argId, edge_1.EdgeType.DefinedByOnCall);
92
+ const pid = param.name.info.id;
93
+ graph.addEdge(argId, pid, edge_1.EdgeType.DefinesOnCall);
94
+ graph.addEdge(pid, argId, edge_1.EdgeType.DefinedByOnCall);
95
+ maps.set(argId, pid);
99
96
  matchedParameters.add(name);
100
97
  }
101
- else if (specialDotParameter?.name) {
102
- graph.addEdge(argId, specialDotParameter.name.info.id, edge_1.EdgeType.DefinesOnCall);
103
- graph.addEdge(specialDotParameter.name.info.id, argId, edge_1.EdgeType.DefinedByOnCall);
98
+ else if (sid) {
99
+ graph.addEdge(argId, sid, edge_1.EdgeType.DefinesOnCall);
100
+ graph.addEdge(sid, argId, edge_1.EdgeType.DefinedByOnCall);
101
+ maps.set(argId, sid);
104
102
  }
105
103
  }
106
104
  const remainingParameter = params.filter(p => !p?.name || !matchedParameters.has(p.name.content));
@@ -110,10 +108,12 @@ function linkArgumentsOnCall(args, params, graph) {
110
108
  if (arg === r_function_call_1.EmptyArgument) {
111
109
  continue;
112
110
  }
111
+ const aid = arg.nodeId;
113
112
  if (remainingParameter.length <= i) {
114
- if (specialDotParameter !== undefined) {
115
- graph.addEdge(arg.nodeId, specialDotParameter.name.info.id, edge_1.EdgeType.DefinesOnCall);
116
- graph.addEdge(specialDotParameter.name.info.id, arg.nodeId, edge_1.EdgeType.DefinedByOnCall);
113
+ if (sid) {
114
+ graph.addEdge(aid, sid, edge_1.EdgeType.DefinesOnCall);
115
+ graph.addEdge(sid, aid, edge_1.EdgeType.DefinedByOnCall);
116
+ maps.set(aid, sid);
117
117
  }
118
118
  else {
119
119
  logger_1.dataflowLogger.warn(`skipping argument ${i} as there is no corresponding parameter - R should block that`);
@@ -121,13 +121,64 @@ function linkArgumentsOnCall(args, params, graph) {
121
121
  continue;
122
122
  }
123
123
  const param = remainingParameter[i];
124
- logger_1.dataflowLogger.trace(`mapping unnamed argument ${i} (id: ${arg.nodeId}) to parameter "${param.name?.content ?? '??'}"`);
124
+ logger_1.dataflowLogger.trace(`mapping unnamed argument ${i} (id: ${aid}) to parameter "${param.name?.content ?? '??'}"`);
125
125
  if (param.name) {
126
- graph.addEdge(arg.nodeId, param.name.info.id, edge_1.EdgeType.DefinesOnCall);
127
- graph.addEdge(param.name.info.id, arg.nodeId, edge_1.EdgeType.DefinedByOnCall);
126
+ const pid = param.name.info.id;
127
+ graph.addEdge(aid, pid, edge_1.EdgeType.DefinesOnCall);
128
+ graph.addEdge(pid, aid, edge_1.EdgeType.DefinedByOnCall);
129
+ maps.set(aid, pid);
128
130
  }
129
131
  }
132
+ return maps;
130
133
  }
134
+ /**
135
+ * Links the given arguments to the given parameters within the given graph by name only.
136
+ * @note
137
+ * To obtain the arguments from a {@link RFunctionCall}[], either use {@link processAllArguments} (also available via {@link processKnownFunctionCall})
138
+ * or convert them with {@link convertFnArguments}.
139
+ */
140
+ function pMatch(args, params) {
141
+ const nameArgMap = new Map(args.filter(graph_1.isNamedArgument).map(a => [a.name, a]));
142
+ const maps = new Map();
143
+ const sid = params['...'];
144
+ const paramNames = Object.keys(params);
145
+ // all parameters matched by name
146
+ const matchedParameters = new Set();
147
+ // first map names
148
+ for (const [name, { nodeId: argId }] of nameArgMap) {
149
+ const pmatchName = (0, prefix_1.findByPrefixIfUnique)(name, paramNames) ?? name;
150
+ const param = params[pmatchName];
151
+ if (param) {
152
+ maps.set(argId, param);
153
+ }
154
+ else if (sid) {
155
+ maps.set(argId, sid);
156
+ }
157
+ }
158
+ const remainingParameter = paramNames.filter(p => !matchedParameters.has(p));
159
+ const remainingArguments = args.filter(a => !(0, graph_1.isNamedArgument)(a));
160
+ for (let i = 0; i < remainingArguments.length; i++) {
161
+ const arg = remainingArguments[i];
162
+ if (arg === r_function_call_1.EmptyArgument) {
163
+ continue;
164
+ }
165
+ const aid = arg.nodeId;
166
+ if (remainingParameter.length <= i) {
167
+ if (sid) {
168
+ maps.set(aid, sid);
169
+ }
170
+ continue;
171
+ }
172
+ const param = params[remainingParameter[i]];
173
+ if (param) {
174
+ maps.set(aid, param);
175
+ }
176
+ }
177
+ return maps;
178
+ }
179
+ /**
180
+ * Links the function call arguments to the target function definition and returns a map from argument ids to parameter ids.
181
+ */
131
182
  function linkFunctionCallArguments(targetId, idMap, functionCallName, functionRootId, callArgs, finalGraph) {
132
183
  // we get them by just choosing the rhs of the definition
133
184
  const linkedFunction = idMap.get(targetId);
@@ -139,16 +190,16 @@ function linkFunctionCallArguments(targetId, idMap, functionCallName, functionRo
139
190
  logger_1.dataflowLogger.trace(`function call definition base ${functionCallName} does not lead to a function definition (${functionRootId}) but got ${linkedFunction.type}`);
140
191
  return;
141
192
  }
142
- linkArgumentsOnCall(callArgs, linkedFunction.parameters, finalGraph);
193
+ return linkArgumentsOnCall(callArgs, linkedFunction.parameters, finalGraph);
143
194
  }
144
195
  /**
145
196
  * Links a function call with a single target function definition.
146
197
  */
147
- function linkFunctionCallWithSingleTarget(graph, def, info, idMap) {
198
+ function linkFunctionCallWithSingleTarget(graph, { subflow: fnSubflow, exitPoints, id: fnId, params }, info, idMap) {
148
199
  const id = info.id;
149
200
  if (info.environment !== undefined) {
150
201
  // for each open ingoing reference, try to resolve it here, and if so, add a read edge from the call to signal that it reads it
151
- for (const ingoing of def.subflow.in) {
202
+ for (const ingoing of fnSubflow.in) {
152
203
  const defs = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, info.environment, ingoing.type) : undefined;
153
204
  if (defs === undefined) {
154
205
  continue;
@@ -161,15 +212,32 @@ function linkFunctionCallWithSingleTarget(graph, def, info, idMap) {
161
212
  }
162
213
  }
163
214
  }
164
- const exitPoints = def.exitPoints;
215
+ const propagateExitPoints = [];
165
216
  for (const exitPoint of exitPoints) {
166
- graph.addEdge(id, exitPoint, edge_1.EdgeType.Returns);
217
+ graph.addEdge(id, exitPoint.nodeId, edge_1.EdgeType.Returns);
218
+ if ((0, info_1.doesExitPointPropagateCalls)(exitPoint.type)) {
219
+ // add the exit point to the call!
220
+ propagateExitPoints.push(exitPoint);
221
+ }
222
+ }
223
+ const defName = (0, node_id_1.recoverName)(fnId, idMap);
224
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `recording expr-list-level call from ${(0, node_id_1.recoverName)(info.id, idMap)} to ${defName}`);
225
+ graph.addEdge(id, fnId, edge_1.EdgeType.Calls);
226
+ applyForForcedArgs(graph, info.id, params, linkFunctionCallArguments(fnId, idMap, defName, id, info.args, graph));
227
+ return propagateExitPoints;
228
+ }
229
+ /** for each parameter that we link that gets forced, add a reads edge from the call to argument to show that it reads it */
230
+ function applyForForcedArgs(graph, callId, readParams, maps) {
231
+ if (maps === undefined) {
232
+ return;
233
+ }
234
+ for (const [arg, param] of maps.entries()) {
235
+ if (readParams[String(param)]) {
236
+ graph.addEdge(callId, arg, edge_1.EdgeType.Reads);
237
+ }
167
238
  }
168
- const defName = (0, node_id_1.recoverName)(def.id, idMap);
169
- (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `recording expression-list-level call from ${(0, node_id_1.recoverName)(info.id, idMap)} to ${defName}`);
170
- graph.addEdge(id, def.id, edge_1.EdgeType.Calls);
171
- linkFunctionCallArguments(def.id, idMap, defName, id, info.args, graph);
172
239
  }
240
+ const FCallLinkReadBits = edge_1.EdgeType.Reads | edge_1.EdgeType.Calls | edge_1.EdgeType.DefinedByOnCall;
173
241
  /* there is _a lot_ potential for optimization here */
174
242
  function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefinitions) {
175
243
  const edges = graph.outgoingEdges(id);
@@ -177,16 +245,26 @@ function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefin
177
245
  /* no outgoing edges */
178
246
  return;
179
247
  }
180
- const readBits = edge_1.EdgeType.Reads | edge_1.EdgeType.Calls;
181
- const functionDefinitionReadIds = [...edges].filter(([_, e]) => (0, edge_1.edgeDoesNotIncludeType)(e.types, edge_1.EdgeType.Argument)
182
- && (0, edge_1.edgeIncludesType)(e.types, readBits)).map(([target, _]) => target);
183
- const functionDefs = getAllLinkedFunctionDefinitions(new Set(functionDefinitionReadIds), graph)[0];
248
+ const functionDefinitionReadIds = new Set();
249
+ for (const [t, { types }] of edges.entries()) {
250
+ if (!(0, built_in_1.isBuiltIn)(t) && (0, edge_1.edgeDoesNotIncludeType)(types, edge_1.EdgeType.Argument) && (0, edge_1.edgeIncludesType)(types, FCallLinkReadBits)) {
251
+ functionDefinitionReadIds.add(t);
252
+ }
253
+ }
254
+ const [functionDefs] = getAllLinkedFunctionDefinitions(new Set(functionDefinitionReadIds), graph);
255
+ const propagateExitPoints = [];
184
256
  for (const def of functionDefs.values()) {
185
- (0, assert_1.guard)(def.tag === vertex_1.VertexType.FunctionDefinition, () => `expected function definition, but got ${def.tag}`);
186
- linkFunctionCallWithSingleTarget(graph, def, info, idMap);
257
+ // we can skip this if we already linked it
258
+ const oEdge = graph.outgoingEdges(id)?.get(def.id);
259
+ if (oEdge && (0, edge_1.edgeIncludesType)(oEdge.types, edge_1.EdgeType.Calls)) {
260
+ continue;
261
+ }
262
+ for (const ep of linkFunctionCallWithSingleTarget(graph, def, info, idMap)) {
263
+ propagateExitPoints.push(ep);
264
+ }
187
265
  }
188
266
  if (thisGraph.isRoot(id) && functionDefs.size > 0) {
189
- calledFunctionDefinitions.push({ functionCall: id, called: [...functionDefs.values()] });
267
+ calledFunctionDefinitions.push({ functionCall: id, called: functionDefs.values().toArray(), propagateExitPoints });
190
268
  }
191
269
  }
192
270
  /**
@@ -199,7 +277,9 @@ function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefin
199
277
  function linkFunctionCalls(graph, idMap, thisGraph) {
200
278
  const calledFunctionDefinitions = [];
201
279
  for (const [id, info] of thisGraph.verticesOfType(vertex_1.VertexType.FunctionCall)) {
202
- linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefinitions);
280
+ if (!info.onlyBuiltin) {
281
+ linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefinitions);
282
+ }
203
283
  }
204
284
  return calledFunctionDefinitions;
205
285
  }
@@ -227,21 +307,25 @@ function getAllFunctionCallTargets(call, graph, environment) {
227
307
  for (const target of functionCallTargets) {
228
308
  found.push(target.id);
229
309
  }
230
- found = found.concat(...builtInTargets, functionCallDefs);
310
+ found = found.concat(Array.from(builtInTargets), functionCallDefs);
231
311
  }
232
312
  return found;
233
313
  }
314
+ const LinkedFnFollowBits = edge_1.EdgeType.Reads | edge_1.EdgeType.DefinedBy | edge_1.EdgeType.DefinedByOnCall;
234
315
  /**
235
316
  * Finds all linked function definitions starting from the given set of read ids.
236
317
  */
237
318
  function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGraph) {
238
- let potential = functionDefinitionReadIds.values().toArray();
239
- const visited = new Set();
240
319
  const result = new Set();
241
320
  const builtIns = new Set();
242
- while (potential.length > 0) {
321
+ if (functionDefinitionReadIds.size === 0) {
322
+ return [result, builtIns];
323
+ }
324
+ const potential = Array.from(functionDefinitionReadIds);
325
+ const visited = new Set();
326
+ while (potential.length !== 0) {
243
327
  const currentId = potential.pop();
244
- // do not traverse builtins further
328
+ visited.add(currentId);
245
329
  if ((0, built_in_1.isBuiltIn)(currentId)) {
246
330
  builtIns.add(currentId);
247
331
  continue;
@@ -250,24 +334,29 @@ function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGrap
250
334
  if (currentInfo === undefined) {
251
335
  continue;
252
336
  }
253
- visited.add(currentId);
254
- const outgoingEdges = currentInfo[1].entries().toArray();
255
- const returnEdges = outgoingEdges
256
- .filter(([_, e]) => (0, edge_1.edgeIncludesType)(e.types, edge_1.EdgeType.Returns));
257
- if (returnEdges.length > 0) {
258
- // only traverse return edges and do not follow `calls` etc. as this indicates that we have a function call which returns a result, and not the function calls itself
259
- potential = potential.concat(returnEdges.map(([target]) => target).filter(id => !visited.has(id)));
337
+ const [vertex, edges] = currentInfo;
338
+ // Found a function definition
339
+ if (vertex.subflow !== undefined) {
340
+ result.add(vertex);
260
341
  continue;
261
342
  }
262
- const followBits = edge_1.EdgeType.Reads | edge_1.EdgeType.DefinedBy | edge_1.EdgeType.DefinedByOnCall;
263
- const followEdges = outgoingEdges.filter(([_, e]) => (0, edge_1.edgeIncludesType)(e.types, followBits));
264
- if (currentInfo[0].subflow !== undefined) {
265
- result.add(currentInfo[0]);
343
+ let hasReturnEdge = false;
344
+ for (const [target, { types }] of edges) {
345
+ if ((0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.Returns)) {
346
+ hasReturnEdge = true;
347
+ if (!visited.has(target)) {
348
+ potential.push(target);
349
+ }
350
+ }
351
+ }
352
+ if (hasReturnEdge) {
353
+ continue;
354
+ }
355
+ for (const [target, { types }] of edges) {
356
+ if ((0, edge_1.edgeIncludesType)(types, LinkedFnFollowBits) && !visited.has(target)) {
357
+ potential.push(target);
358
+ }
266
359
  }
267
- // trace all joined reads
268
- potential = potential.concat(followEdges
269
- .map(([target]) => target)
270
- .filter(id => !visited.has(id)));
271
360
  }
272
361
  return [result, builtIns];
273
362
  }
@@ -327,10 +416,10 @@ function linkCircularRedefinitionsWithinALoop(graph, openIns, outgoing) {
327
416
  }
328
417
  }
329
418
  for (const [name, targets] of openIns.entries()) {
330
- for (const out of lastOutgoing.values()) {
331
- if (out.name === name) {
419
+ for (const { name: outName, nodeId } of lastOutgoing.values()) {
420
+ if (outName === name) {
332
421
  for (const target of targets) {
333
- graph.addEdge(target.nodeId, out.nodeId, edge_1.EdgeType.Reads);
422
+ graph.addEdge(target.nodeId, nodeId, edge_1.EdgeType.Reads);
334
423
  }
335
424
  }
336
425
  }
@@ -344,8 +433,9 @@ function reapplyLoopExitPoints(exits, references) {
344
433
  const exitCds = new Set(exits.flatMap(e => e.controlDependencies).filter(assert_1.isNotUndefined));
345
434
  for (const ref of references) {
346
435
  for (const cd of exitCds) {
436
+ const { id: cId, when: cWhen } = cd;
347
437
  if (ref.controlDependencies) {
348
- if (!ref.controlDependencies?.find(c => c.id === cd.id && c.when === cd.when)) {
438
+ if (!ref.controlDependencies?.find(c => c.id === cId && c.when === cWhen)) {
349
439
  ref.controlDependencies.push({ ...cd, byIteration: true });
350
440
  }
351
441
  }