@eagleoutice/flowr 2.7.6 → 2.8.0

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 +614 -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 +209 -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 +155 -53
  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 -28
  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
  */
@@ -81,26 +83,34 @@ function produceNameSharedIdMap(references) {
81
83
  * Links the given arguments to the given parameters within the given graph.
82
84
  * This follows the `pmatch` semantics of R
83
85
  * @see https://cran.r-project.org/doc/manuals/R-lang.html#Argument-matching
86
+ * This returns the resolved map from argument ids to parameter ids.
87
+ * If you just want to match by name, use {@link pMatch}.
84
88
  */
85
89
  function linkArgumentsOnCall(args, params, graph) {
86
90
  const nameArgMap = new Map(args.filter(graph_1.isNamedArgument).map(a => [a.name, a]));
87
91
  const nameParamMap = new Map(params.filter(p => p?.name?.content !== undefined)
88
92
  .map(p => [p.name.content, p]));
93
+ const maps = new Map();
89
94
  const specialDotParameter = params.find(p => p.special);
95
+ const sid = specialDotParameter?.name.info.id;
90
96
  // all parameters matched by name
91
97
  const matchedParameters = new Set();
98
+ const paramNames = nameParamMap.keys().toArray();
92
99
  // first map names
93
100
  for (const [name, { nodeId: argId }] of nameArgMap) {
94
- const pmatchName = (0, prefix_1.findByPrefixIfUnique)(name, nameParamMap.keys()) ?? name;
101
+ const pmatchName = (0, prefix_1.findByPrefixIfUnique)(name, paramNames) ?? name;
95
102
  const param = nameParamMap.get(pmatchName);
96
103
  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);
104
+ const pid = param.name.info.id;
105
+ graph.addEdge(argId, pid, edge_1.EdgeType.DefinesOnCall);
106
+ graph.addEdge(pid, argId, edge_1.EdgeType.DefinedByOnCall);
107
+ maps.set(argId, pid);
99
108
  matchedParameters.add(name);
100
109
  }
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);
110
+ else if (sid) {
111
+ graph.addEdge(argId, sid, edge_1.EdgeType.DefinesOnCall);
112
+ graph.addEdge(sid, argId, edge_1.EdgeType.DefinedByOnCall);
113
+ maps.set(argId, sid);
104
114
  }
105
115
  }
106
116
  const remainingParameter = params.filter(p => !p?.name || !matchedParameters.has(p.name.content));
@@ -110,10 +120,12 @@ function linkArgumentsOnCall(args, params, graph) {
110
120
  if (arg === r_function_call_1.EmptyArgument) {
111
121
  continue;
112
122
  }
123
+ const aid = arg.nodeId;
113
124
  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);
125
+ if (sid) {
126
+ graph.addEdge(aid, sid, edge_1.EdgeType.DefinesOnCall);
127
+ graph.addEdge(sid, aid, edge_1.EdgeType.DefinedByOnCall);
128
+ maps.set(aid, sid);
117
129
  }
118
130
  else {
119
131
  logger_1.dataflowLogger.warn(`skipping argument ${i} as there is no corresponding parameter - R should block that`);
@@ -121,13 +133,64 @@ function linkArgumentsOnCall(args, params, graph) {
121
133
  continue;
122
134
  }
123
135
  const param = remainingParameter[i];
124
- logger_1.dataflowLogger.trace(`mapping unnamed argument ${i} (id: ${arg.nodeId}) to parameter "${param.name?.content ?? '??'}"`);
136
+ logger_1.dataflowLogger.trace(`mapping unnamed argument ${i} (id: ${aid}) to parameter "${param.name?.content ?? '??'}"`);
125
137
  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);
138
+ const pid = param.name.info.id;
139
+ graph.addEdge(aid, pid, edge_1.EdgeType.DefinesOnCall);
140
+ graph.addEdge(pid, aid, edge_1.EdgeType.DefinedByOnCall);
141
+ maps.set(aid, pid);
128
142
  }
129
143
  }
144
+ return maps;
130
145
  }
146
+ /**
147
+ * Links the given arguments to the given parameters within the given graph by name only.
148
+ * @note
149
+ * To obtain the arguments from a {@link RFunctionCall}[], either use {@link processAllArguments} (also available via {@link processKnownFunctionCall})
150
+ * or convert them with {@link convertFnArguments}.
151
+ */
152
+ function pMatch(args, params) {
153
+ const nameArgMap = new Map(args.filter(graph_1.isNamedArgument).map(a => [a.name, a]));
154
+ const maps = new Map();
155
+ const sid = params['...'];
156
+ const paramNames = Object.keys(params);
157
+ // all parameters matched by name
158
+ const matchedParameters = new Set();
159
+ // first map names
160
+ for (const [name, { nodeId: argId }] of nameArgMap) {
161
+ const pmatchName = (0, prefix_1.findByPrefixIfUnique)(name, paramNames) ?? name;
162
+ const param = params[pmatchName];
163
+ if (param) {
164
+ maps.set(argId, param);
165
+ }
166
+ else if (sid) {
167
+ maps.set(argId, sid);
168
+ }
169
+ }
170
+ const remainingParameter = paramNames.filter(p => !matchedParameters.has(p));
171
+ const remainingArguments = args.filter(a => !(0, graph_1.isNamedArgument)(a));
172
+ for (let i = 0; i < remainingArguments.length; i++) {
173
+ const arg = remainingArguments[i];
174
+ if (arg === r_function_call_1.EmptyArgument) {
175
+ continue;
176
+ }
177
+ const aid = arg.nodeId;
178
+ if (remainingParameter.length <= i) {
179
+ if (sid) {
180
+ maps.set(aid, sid);
181
+ }
182
+ continue;
183
+ }
184
+ const param = params[remainingParameter[i]];
185
+ if (param) {
186
+ maps.set(aid, param);
187
+ }
188
+ }
189
+ return maps;
190
+ }
191
+ /**
192
+ * Links the function call arguments to the target function definition and returns a map from argument ids to parameter ids.
193
+ */
131
194
  function linkFunctionCallArguments(targetId, idMap, functionCallName, functionRootId, callArgs, finalGraph) {
132
195
  // we get them by just choosing the rhs of the definition
133
196
  const linkedFunction = idMap.get(targetId);
@@ -139,16 +202,16 @@ function linkFunctionCallArguments(targetId, idMap, functionCallName, functionRo
139
202
  logger_1.dataflowLogger.trace(`function call definition base ${functionCallName} does not lead to a function definition (${functionRootId}) but got ${linkedFunction.type}`);
140
203
  return;
141
204
  }
142
- linkArgumentsOnCall(callArgs, linkedFunction.parameters, finalGraph);
205
+ return linkArgumentsOnCall(callArgs, linkedFunction.parameters, finalGraph);
143
206
  }
144
207
  /**
145
208
  * Links a function call with a single target function definition.
146
209
  */
147
- function linkFunctionCallWithSingleTarget(graph, def, info, idMap) {
210
+ function linkFunctionCallWithSingleTarget(graph, { subflow: fnSubflow, exitPoints, id: fnId, params }, info, idMap) {
148
211
  const id = info.id;
149
212
  if (info.environment !== undefined) {
150
213
  // 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) {
214
+ for (const ingoing of fnSubflow.in) {
152
215
  const defs = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, info.environment, ingoing.type) : undefined;
153
216
  if (defs === undefined) {
154
217
  continue;
@@ -161,15 +224,32 @@ function linkFunctionCallWithSingleTarget(graph, def, info, idMap) {
161
224
  }
162
225
  }
163
226
  }
164
- const exitPoints = def.exitPoints;
227
+ const propagateExitPoints = [];
165
228
  for (const exitPoint of exitPoints) {
166
- graph.addEdge(id, exitPoint, edge_1.EdgeType.Returns);
229
+ graph.addEdge(id, exitPoint.nodeId, edge_1.EdgeType.Returns);
230
+ if ((0, info_1.doesExitPointPropagateCalls)(exitPoint.type)) {
231
+ // add the exit point to the call!
232
+ propagateExitPoints.push(exitPoint);
233
+ }
234
+ }
235
+ const defName = (0, node_id_1.recoverName)(fnId, idMap);
236
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `recording expr-list-level call from ${(0, node_id_1.recoverName)(info.id, idMap)} to ${defName}`);
237
+ graph.addEdge(id, fnId, edge_1.EdgeType.Calls);
238
+ applyForForcedArgs(graph, info.id, params, linkFunctionCallArguments(fnId, idMap, defName, id, info.args, graph));
239
+ return propagateExitPoints;
240
+ }
241
+ /** for each parameter that we link that gets forced, add a reads edge from the call to argument to show that it reads it */
242
+ function applyForForcedArgs(graph, callId, readParams, maps) {
243
+ if (maps === undefined) {
244
+ return;
245
+ }
246
+ for (const [arg, param] of maps.entries()) {
247
+ if (readParams[String(param)]) {
248
+ graph.addEdge(callId, arg, edge_1.EdgeType.Reads);
249
+ }
167
250
  }
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
251
  }
252
+ const FCallLinkReadBits = edge_1.EdgeType.Reads | edge_1.EdgeType.Calls | edge_1.EdgeType.DefinedByOnCall;
173
253
  /* there is _a lot_ potential for optimization here */
174
254
  function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefinitions) {
175
255
  const edges = graph.outgoingEdges(id);
@@ -177,16 +257,26 @@ function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefin
177
257
  /* no outgoing edges */
178
258
  return;
179
259
  }
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];
260
+ const functionDefinitionReadIds = new Set();
261
+ for (const [t, { types }] of edges.entries()) {
262
+ if (!(0, built_in_1.isBuiltIn)(t) && (0, edge_1.edgeDoesNotIncludeType)(types, edge_1.EdgeType.Argument) && (0, edge_1.edgeIncludesType)(types, FCallLinkReadBits)) {
263
+ functionDefinitionReadIds.add(t);
264
+ }
265
+ }
266
+ const [functionDefs] = getAllLinkedFunctionDefinitions(new Set(functionDefinitionReadIds), graph);
267
+ const propagateExitPoints = [];
184
268
  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);
269
+ // we can skip this if we already linked it
270
+ const oEdge = graph.outgoingEdges(id)?.get(def.id);
271
+ if (oEdge && (0, edge_1.edgeIncludesType)(oEdge.types, edge_1.EdgeType.Calls)) {
272
+ continue;
273
+ }
274
+ for (const ep of linkFunctionCallWithSingleTarget(graph, def, info, idMap)) {
275
+ propagateExitPoints.push(ep);
276
+ }
187
277
  }
188
278
  if (thisGraph.isRoot(id) && functionDefs.size > 0) {
189
- calledFunctionDefinitions.push({ functionCall: id, called: [...functionDefs.values()] });
279
+ calledFunctionDefinitions.push({ functionCall: id, called: functionDefs.values().toArray(), propagateExitPoints });
190
280
  }
191
281
  }
192
282
  /**
@@ -199,7 +289,9 @@ function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefin
199
289
  function linkFunctionCalls(graph, idMap, thisGraph) {
200
290
  const calledFunctionDefinitions = [];
201
291
  for (const [id, info] of thisGraph.verticesOfType(vertex_1.VertexType.FunctionCall)) {
202
- linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefinitions);
292
+ if (!info.onlyBuiltin) {
293
+ linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefinitions);
294
+ }
203
295
  }
204
296
  return calledFunctionDefinitions;
205
297
  }
@@ -227,21 +319,25 @@ function getAllFunctionCallTargets(call, graph, environment) {
227
319
  for (const target of functionCallTargets) {
228
320
  found.push(target.id);
229
321
  }
230
- found = found.concat(...builtInTargets, functionCallDefs);
322
+ found = found.concat(Array.from(builtInTargets), functionCallDefs);
231
323
  }
232
324
  return found;
233
325
  }
326
+ const LinkedFnFollowBits = edge_1.EdgeType.Reads | edge_1.EdgeType.DefinedBy | edge_1.EdgeType.DefinedByOnCall;
234
327
  /**
235
328
  * Finds all linked function definitions starting from the given set of read ids.
236
329
  */
237
330
  function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGraph) {
238
- let potential = functionDefinitionReadIds.values().toArray();
239
- const visited = new Set();
240
331
  const result = new Set();
241
332
  const builtIns = new Set();
242
- while (potential.length > 0) {
333
+ if (functionDefinitionReadIds.size === 0) {
334
+ return [result, builtIns];
335
+ }
336
+ const potential = Array.from(functionDefinitionReadIds);
337
+ const visited = new Set();
338
+ while (potential.length !== 0) {
243
339
  const currentId = potential.pop();
244
- // do not traverse builtins further
340
+ visited.add(currentId);
245
341
  if ((0, built_in_1.isBuiltIn)(currentId)) {
246
342
  builtIns.add(currentId);
247
343
  continue;
@@ -250,24 +346,29 @@ function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGrap
250
346
  if (currentInfo === undefined) {
251
347
  continue;
252
348
  }
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)));
349
+ const [vertex, edges] = currentInfo;
350
+ // Found a function definition
351
+ if (vertex.subflow !== undefined) {
352
+ result.add(vertex);
260
353
  continue;
261
354
  }
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]);
355
+ let hasReturnEdge = false;
356
+ for (const [target, { types }] of edges) {
357
+ if ((0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.Returns)) {
358
+ hasReturnEdge = true;
359
+ if (!visited.has(target)) {
360
+ potential.push(target);
361
+ }
362
+ }
363
+ }
364
+ if (hasReturnEdge) {
365
+ continue;
366
+ }
367
+ for (const [target, { types }] of edges) {
368
+ if ((0, edge_1.edgeIncludesType)(types, LinkedFnFollowBits) && !visited.has(target)) {
369
+ potential.push(target);
370
+ }
266
371
  }
267
- // trace all joined reads
268
- potential = potential.concat(followEdges
269
- .map(([target]) => target)
270
- .filter(id => !visited.has(id)));
271
372
  }
272
373
  return [result, builtIns];
273
374
  }
@@ -327,10 +428,10 @@ function linkCircularRedefinitionsWithinALoop(graph, openIns, outgoing) {
327
428
  }
328
429
  }
329
430
  for (const [name, targets] of openIns.entries()) {
330
- for (const out of lastOutgoing.values()) {
331
- if (out.name === name) {
431
+ for (const { name: outName, nodeId } of lastOutgoing.values()) {
432
+ if (outName === name) {
332
433
  for (const target of targets) {
333
- graph.addEdge(target.nodeId, out.nodeId, edge_1.EdgeType.Reads);
434
+ graph.addEdge(target.nodeId, nodeId, edge_1.EdgeType.Reads);
334
435
  }
335
436
  }
336
437
  }
@@ -344,8 +445,9 @@ function reapplyLoopExitPoints(exits, references) {
344
445
  const exitCds = new Set(exits.flatMap(e => e.controlDependencies).filter(assert_1.isNotUndefined));
345
446
  for (const ref of references) {
346
447
  for (const cd of exitCds) {
448
+ const { id: cId, when: cWhen } = cd;
347
449
  if (ref.controlDependencies) {
348
- if (!ref.controlDependencies?.find(c => c.id === cd.id && c.when === cd.when)) {
450
+ if (!ref.controlDependencies?.find(c => c.id === cId && c.when === cWhen)) {
349
451
  ref.controlDependencies.push({ ...cd, byIteration: true });
350
452
  }
351
453
  }
@@ -10,3 +10,7 @@ export declare function unpackNonameArg<OtherInfo>(arg: RFunctionArgument<OtherI
10
10
  * @see {@link unpackNonameArg} - to specifically retrieve non-named arguments
11
11
  */
12
12
  export declare function unpackArg<OtherInfo>(arg: RFunctionArgument<OtherInfo> | undefined): RNode<OtherInfo> | undefined;
13
+ /**
14
+ * Try to unpack the given argument, if it is not empty.
15
+ */
16
+ export declare function tryUnpackNoNameArg<OtherInfo>(arg: RFunctionArgument<OtherInfo>): RNode<OtherInfo> | RFunctionArgument<OtherInfo>;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.unpackNonameArg = unpackNonameArg;
4
4
  exports.unpackArg = unpackArg;
5
+ exports.tryUnpackNoNameArg = tryUnpackNoNameArg;
5
6
  const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
6
7
  /**
7
8
  * Retrieve the value from an argument, if it is not empty.
@@ -17,4 +18,10 @@ function unpackNonameArg(arg) {
17
18
  function unpackArg(arg) {
18
19
  return arg === r_function_call_1.EmptyArgument ? undefined : arg?.value;
19
20
  }
21
+ /**
22
+ * Try to unpack the given argument, if it is not empty.
23
+ */
24
+ function tryUnpackNoNameArg(arg) {
25
+ return unpackNonameArg(arg) ?? arg;
26
+ }
20
27
  //# sourceMappingURL=unpack-argument.js.map
@@ -13,7 +13,7 @@ export interface BuiltInApplyConfiguration extends MergeableRecord {
13
13
  /** Should we unquote the function if it is given as a string? */
14
14
  readonly unquoteFunction?: boolean;
15
15
  /** Should the function be resolved in the global environment? */
16
- readonly resolveInEnvironment: 'global' | 'local';
16
+ readonly resolveInEnvironment?: 'global' | 'local';
17
17
  /** Should the value of the function be resolved? */
18
18
  readonly resolveValue?: boolean;
19
19
  }