@eagleoutice/flowr 2.2.14 → 2.2.15

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 (83) hide show
  1. package/README.md +4 -4
  2. package/cli/repl/commands/repl-commands.js +1 -1
  3. package/cli/repl/commands/repl-execute.js +2 -1
  4. package/config.js +1 -1
  5. package/control-flow/basic-cfg-guided-visitor.d.ts +3 -3
  6. package/control-flow/cfg-dead-code.d.ts +4 -0
  7. package/control-flow/cfg-dead-code.js +81 -0
  8. package/control-flow/cfg-simplification.d.ts +17 -6
  9. package/control-flow/cfg-simplification.js +23 -19
  10. package/control-flow/control-flow-graph.d.ts +2 -1
  11. package/control-flow/control-flow-graph.js +1 -0
  12. package/control-flow/dfg-cfg-guided-visitor.d.ts +4 -4
  13. package/control-flow/dfg-cfg-guided-visitor.js +1 -1
  14. package/control-flow/extract-cfg.d.ts +1 -1
  15. package/control-flow/extract-cfg.js +60 -57
  16. package/control-flow/semantic-cfg-guided-visitor.d.ts +17 -8
  17. package/control-flow/semantic-cfg-guided-visitor.js +50 -17
  18. package/control-flow/simple-visitor.d.ts +4 -0
  19. package/control-flow/simple-visitor.js +14 -0
  20. package/control-flow/syntax-cfg-guided-visitor.d.ts +2 -2
  21. package/dataflow/environments/built-in-config.d.ts +1 -0
  22. package/dataflow/environments/built-in.d.ts +10 -1
  23. package/dataflow/environments/built-in.js +9 -3
  24. package/dataflow/environments/default-builtin-config.js +1 -1
  25. package/dataflow/environments/resolve-by-name.d.ts +0 -36
  26. package/dataflow/environments/resolve-by-name.js +0 -240
  27. package/dataflow/eval/resolve/alias-tracking.d.ts +87 -0
  28. package/dataflow/eval/resolve/alias-tracking.js +349 -0
  29. package/dataflow/eval/resolve/resolve.d.ts +34 -0
  30. package/dataflow/eval/resolve/resolve.js +93 -0
  31. package/dataflow/eval/values/general.d.ts +27 -0
  32. package/dataflow/eval/values/general.js +73 -0
  33. package/dataflow/eval/values/intervals/interval-constants.d.ts +4 -0
  34. package/dataflow/eval/values/intervals/interval-constants.js +27 -0
  35. package/dataflow/eval/values/logical/logical-constants.d.ts +7 -0
  36. package/dataflow/eval/values/logical/logical-constants.js +31 -0
  37. package/dataflow/eval/values/r-value.d.ts +58 -0
  38. package/dataflow/eval/values/r-value.js +90 -0
  39. package/dataflow/eval/values/scalar/scalar-consatnts.d.ts +15 -0
  40. package/dataflow/eval/values/scalar/scalar-consatnts.js +35 -0
  41. package/dataflow/eval/values/sets/set-constants.d.ts +7 -0
  42. package/dataflow/eval/values/sets/set-constants.js +34 -0
  43. package/dataflow/eval/values/string/string-constants.d.ts +8 -0
  44. package/dataflow/eval/values/string/string-constants.js +40 -0
  45. package/dataflow/eval/values/vectors/vector-constants.d.ts +14 -0
  46. package/dataflow/eval/values/vectors/vector-constants.js +35 -0
  47. package/dataflow/graph/unknown-replacement.d.ts +11 -0
  48. package/dataflow/graph/unknown-replacement.js +12 -0
  49. package/dataflow/graph/unknown-side-effect.d.ts +7 -0
  50. package/dataflow/graph/unknown-side-effect.js +13 -0
  51. package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +8 -5
  52. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +4 -2
  53. package/dataflow/internal/process/functions/call/built-in/built-in-eval.js +12 -9
  54. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +5 -4
  55. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +9 -2
  56. package/dataflow/internal/process/functions/call/built-in/built-in-source.js +12 -15
  57. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +23 -0
  58. package/dataflow/internal/process/functions/call/known-call-handling.js +2 -1
  59. package/documentation/doc-util/doc-query.d.ts +6 -3
  60. package/documentation/doc-util/doc-query.js +3 -1
  61. package/documentation/print-cfg-wiki.js +6 -6
  62. package/documentation/print-dataflow-graph-wiki.js +4 -3
  63. package/documentation/print-engines-wiki.js +1 -1
  64. package/documentation/print-query-wiki.js +80 -0
  65. package/linter/rules/1-deprecated-functions.js +1 -1
  66. package/linter/rules/2-file-path-validity.js +1 -1
  67. package/package.json +1 -1
  68. package/queries/catalog/control-flow-query/control-flow-query-executor.d.ts +3 -0
  69. package/queries/catalog/control-flow-query/control-flow-query-executor.js +20 -0
  70. package/queries/catalog/control-flow-query/control-flow-query-format.d.ts +81 -0
  71. package/queries/catalog/control-flow-query/control-flow-query-format.js +34 -0
  72. package/queries/catalog/dependencies-query/dependencies-query-executor.js +33 -32
  73. package/queries/catalog/linter-query/linter-query-format.js +2 -1
  74. package/queries/catalog/resolve-value-query/resolve-value-query-executor.js +3 -3
  75. package/queries/catalog/resolve-value-query/resolve-value-query-format.d.ts +2 -1
  76. package/queries/catalog/resolve-value-query/resolve-value-query-format.js +2 -22
  77. package/queries/query.d.ts +61 -1
  78. package/queries/query.js +2 -0
  79. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +11 -4
  80. package/search/search-executor/search-enrichers.js +5 -2
  81. package/slicing/criterion/parse.d.ts +8 -0
  82. package/slicing/criterion/parse.js +20 -0
  83. package/util/version.js +1 -1
package/README.md CHANGED
@@ -51,7 +51,7 @@ It offers a wide variety of features, for example:
51
51
 
52
52
  ```shell
53
53
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
54
- flowR repl using flowR v2.2.13, R v4.4.3 (r-shell engine)
54
+ flowR repl using flowR v2.2.14, R v4.4.3 (r-shell engine)
55
55
  R> :slicer test/testfiles/example.R --criterion "11@sum"
56
56
  ```
57
57
 
@@ -98,7 +98,7 @@ It offers a wide variety of features, for example:
98
98
 
99
99
 
100
100
  * 🚀 **fast data- and control-flow graphs**\
101
- Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">139.5 ms</span></i> (as of May 27, 2025),
101
+ Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">133.8 ms</span></i> (as of May 31, 2025),
102
102
  _flowR_ can analyze the data- and control-flow of the average real-world R script. See the [benchmarks](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark) for more information,
103
103
  and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/Dataflow-Graph) for more details on the dataflow graph.
104
104
 
@@ -134,7 +134,7 @@ It offers a wide variety of features, for example:
134
134
 
135
135
  ```shell
136
136
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
137
- flowR repl using flowR v2.2.13, R v4.4.3 (r-shell engine)
137
+ flowR repl using flowR v2.2.14, R v4.4.3 (r-shell engine)
138
138
  R> :dataflow* test/testfiles/example.R
139
139
  ```
140
140
 
@@ -435,7 +435,7 @@ It offers a wide variety of features, for example:
435
435
  ```
436
436
 
437
437
 
438
- (The analysis required _22.0 ms_ (including parse and normalize, using the [r-shell](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
438
+ (The analysis required _22.8 ms_ (including parse and normalize, using the [r-shell](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
439
439
 
440
440
 
441
441
 
@@ -53,7 +53,7 @@ exports.helpCommand = {
53
53
  fn: output => {
54
54
  initCommandMapping();
55
55
  output.stdout(`
56
- If enabled ('--r-session-access'), you can just enter R expressions which get evaluated right away:
56
+ If enabled ('--r-session-access' and if using the 'r-shell' engine), you can just enter R expressions which get evaluated right away:
57
57
  ${prompt_1.rawPrompt} ${(0, ansi_1.bold)('1 + 1', output.formatter)}
58
58
  ${(0, ansi_1.italic)('[1] 2', output.formatter)}
59
59
 
@@ -6,7 +6,8 @@ const ansi_1 = require("../../../util/text/ansi");
6
6
  const shell_1 = require("../../../r-bridge/shell");
7
7
  async function tryExecuteRShellCommand(output, parser, statement, allowRSessionAccess) {
8
8
  if (!allowRSessionAccess) {
9
- output.stderr(`${output.formatter.format('You are not allowed to execute arbitrary R code.', { style: 1 /* FontStyles.Bold */, color: 1 /* Colors.Red */, effect: ansi_1.ColorEffect.Foreground })}\nIf you want to do so, please restart flowR with the ${output.formatter.format('--r-session-access', { style: 1 /* FontStyles.Bold */ })} flag. Please be careful of the security implications of this action.`);
9
+ output.stderr(`${output.formatter.format('You are not allowed to execute arbitrary R code.', { style: 1 /* FontStyles.Bold */, color: 1 /* Colors.Red */, effect: ansi_1.ColorEffect.Foreground })}
10
+ If you want to do so, please restart flowR with the ${output.formatter.format('--r-session-access', { style: 1 /* FontStyles.Bold */ })} flag${parser.name !== 'r-shell' ? '. Additionally, please enable the r-shell engine, e.g., with ' + output.formatter.format('--default-engine r-shell', { style: 1 /* FontStyles.Bold */ }) : ''}. Please be careful of the security implications of this action.`);
10
11
  }
11
12
  else if (parser instanceof shell_1.RShell) {
12
13
  await executeRShellCommand(output, parser, statement);
package/config.js CHANGED
@@ -67,7 +67,7 @@ exports.defaultConfigOptions = {
67
67
  }
68
68
  },
69
69
  engines: [],
70
- defaultEngine: 'r-shell',
70
+ defaultEngine: 'tree-sitter',
71
71
  solver: {
72
72
  variables: VariableResolve.Alias,
73
73
  evalStrings: true,
@@ -1,7 +1,7 @@
1
1
  import type { CfgBasicBlockVertex, CfgEndMarkerVertex, CfgExpressionVertex, CfgMidMarkerVertex, CfgSimpleVertex, CfgStatementVertex, ControlFlowInformation } from './control-flow-graph';
2
2
  import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id';
3
- export interface BasicCfgGuidedVisitorConfiguration<Cfg extends ControlFlowInformation = ControlFlowInformation> {
4
- readonly controlFlow: Cfg;
3
+ export interface BasicCfgGuidedVisitorConfiguration<ControlFlow extends ControlFlowInformation = ControlFlowInformation> {
4
+ readonly controlFlow: ControlFlow;
5
5
  readonly defaultVisitingOrder: 'forward' | 'backward';
6
6
  }
7
7
  /**
@@ -11,7 +11,7 @@ export interface BasicCfgGuidedVisitorConfiguration<Cfg extends ControlFlowInfor
11
11
  *
12
12
  * Use {@link BasicCfgGuidedVisitor#start} to start the traversal.
13
13
  */
14
- export declare class BasicCfgGuidedVisitor<Cfg extends ControlFlowInformation = ControlFlowInformation, Config extends BasicCfgGuidedVisitorConfiguration<Cfg> = BasicCfgGuidedVisitorConfiguration<Cfg>> {
14
+ export declare class BasicCfgGuidedVisitor<ControlFlow extends ControlFlowInformation = ControlFlowInformation, Config extends BasicCfgGuidedVisitorConfiguration<ControlFlow> = BasicCfgGuidedVisitorConfiguration<ControlFlow>> {
15
15
  protected readonly config: Config;
16
16
  protected readonly visited: Map<NodeId, number>;
17
17
  constructor(config: Config);
@@ -0,0 +1,4 @@
1
+ import type { ControlFlowInformation } from './control-flow-graph';
2
+ import type { CfgPassInfo } from './cfg-simplification';
3
+ /** Breaks unsatisfiable control dependencies */
4
+ export declare function cfgAnalyzeDeadCode(cfg: ControlFlowInformation, info: CfgPassInfo): ControlFlowInformation;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cfgAnalyzeDeadCode = cfgAnalyzeDeadCode;
4
+ const logic_1 = require("../util/logic");
5
+ const semantic_cfg_guided_visitor_1 = require("./semantic-cfg-guided-visitor");
6
+ const alias_tracking_1 = require("../dataflow/eval/resolve/alias-tracking");
7
+ const log_1 = require("../util/log");
8
+ const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
9
+ const general_1 = require("../dataflow/eval/values/general");
10
+ const r_value_1 = require("../dataflow/eval/values/r-value");
11
+ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor {
12
+ cachedConditions = new Map();
13
+ getValue(id) {
14
+ const has = this.cachedConditions.get(id);
15
+ if (has) {
16
+ return has;
17
+ }
18
+ this.visitNode(id);
19
+ return this.cachedConditions.get(id) ?? logic_1.Ternary.Maybe;
20
+ }
21
+ unableToCalculateValue(id) {
22
+ this.cachedConditions.set(id, logic_1.Ternary.Maybe);
23
+ }
24
+ storeDefiniteValue(id, value) {
25
+ this.cachedConditions.set(id, value ? logic_1.Ternary.Always : logic_1.Ternary.Never);
26
+ }
27
+ startVisitor() {
28
+ for (const [from, targets] of this.config.controlFlow.graph.edges()) {
29
+ for (const [target, edge] of targets) {
30
+ if (edge.label === 1 /* CfgEdgeType.Cd */) {
31
+ const og = this.getValue(edge.caused);
32
+ if (og === logic_1.Ternary.Always && edge.when === 'FALSE') {
33
+ this.config.controlFlow.graph.removeEdge(from, target);
34
+ }
35
+ else if (og === logic_1.Ternary.Never && edge.when === 'TRUE') {
36
+ this.config.controlFlow.graph.removeEdge(from, target);
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ handleValuesFor(id, valueId) {
43
+ const values = (0, general_1.valueSetGuard)((0, alias_tracking_1.resolveIdToValue)(valueId, { graph: this.config.dfg, full: true, idMap: this.config.normalizedAst.idMap }));
44
+ if (values === undefined || values.elements.length !== 1 || values.elements[0].type != 'logical' || !(0, r_value_1.isValue)(values.elements[0].value)) {
45
+ this.unableToCalculateValue(id);
46
+ return;
47
+ }
48
+ /* we should translate this to truthy later */
49
+ this.storeDefiniteValue(id, Boolean(values.elements[0].value));
50
+ }
51
+ handleWithCondition(data) {
52
+ const id = data.call.id;
53
+ if (data.condition === undefined || data.condition === r_function_call_1.EmptyArgument) {
54
+ this.unableToCalculateValue(id);
55
+ return;
56
+ }
57
+ this.handleValuesFor(id, typeof data.condition === 'object' ? data.condition.nodeId : data.condition);
58
+ }
59
+ onIfThenElseCall(data) {
60
+ this.handleWithCondition(data);
61
+ }
62
+ onWhileLoopCall(data) {
63
+ this.handleWithCondition(data);
64
+ }
65
+ }
66
+ /** Breaks unsatisfiable control dependencies */
67
+ function cfgAnalyzeDeadCode(cfg, info) {
68
+ if (!info.ast || !info.dfg) {
69
+ log_1.log.warn('cfgAnalyzeDeadCode called without ast or dfg, skipping dead code analysis');
70
+ return cfg;
71
+ }
72
+ const visitor = new CfgConditionalDeadCodeRemoval({
73
+ controlFlow: cfg,
74
+ normalizedAst: info.ast,
75
+ dfg: info.dfg,
76
+ defaultVisitingOrder: 'forward'
77
+ });
78
+ visitor.start();
79
+ return cfg;
80
+ }
81
+ //# sourceMappingURL=cfg-dead-code.js.map
@@ -1,7 +1,15 @@
1
1
  import type { ControlFlowInformation } from './control-flow-graph';
2
- export type CfgSimplificationPass = (cfg: ControlFlowInformation) => ControlFlowInformation;
3
- declare const CfgSimplificationPasses: {
2
+ import type { NormalizedAst } from '../r-bridge/lang-4.x/ast/model/processing/decorate';
3
+ import type { DataflowGraph } from '../dataflow/graph/graph';
4
+ import { cfgAnalyzeDeadCode } from './cfg-dead-code';
5
+ export interface CfgPassInfo {
6
+ ast?: NormalizedAst;
7
+ dfg?: DataflowGraph;
8
+ }
9
+ export type CfgSimplificationPass = (cfg: ControlFlowInformation, info: CfgPassInfo) => ControlFlowInformation;
10
+ export declare const CfgSimplificationPasses: {
4
11
  readonly 'unique-cf-sets': typeof uniqueControlFlowSets;
12
+ readonly 'analyze-dead-code': typeof cfgAnalyzeDeadCode;
5
13
  readonly 'remove-dead-code': typeof cfgRemoveDeadCode;
6
14
  readonly 'to-basic-blocks': typeof toBasicBlocks;
7
15
  };
@@ -11,8 +19,11 @@ export declare const DefaultCfgSimplificationOrder: ["unique-cf-sets"];
11
19
  * Simplify the control flow information by applying the given passes.
12
20
  * This may reduce the vertex count, in- and outgoing edges, entry and exit points, etc.
13
21
  */
14
- export declare function simplifyControlFlowInformation(cfg: ControlFlowInformation, passes?: readonly CfgSimplificationPassName[]): ControlFlowInformation;
15
- declare function uniqueControlFlowSets(cfg: ControlFlowInformation): ControlFlowInformation;
16
- declare function cfgRemoveDeadCode(cfg: ControlFlowInformation): ControlFlowInformation;
17
- declare function toBasicBlocks(cfg: ControlFlowInformation): ControlFlowInformation;
22
+ export declare function simplifyControlFlowInformation(cfg: ControlFlowInformation, info: CfgPassInfo, passes?: readonly CfgSimplificationPassName[]): ControlFlowInformation;
23
+ /**
24
+ * removes dead vertices and edges from the control flow graph.
25
+ */
26
+ declare function cfgRemoveDeadCode(cfg: ControlFlowInformation, _info?: CfgPassInfo): ControlFlowInformation;
27
+ declare function uniqueControlFlowSets(cfg: ControlFlowInformation, _info?: CfgPassInfo): ControlFlowInformation;
28
+ declare function toBasicBlocks(cfg: ControlFlowInformation, _info?: CfgPassInfo): ControlFlowInformation;
18
29
  export {};
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DefaultCfgSimplificationOrder = void 0;
3
+ exports.DefaultCfgSimplificationOrder = exports.CfgSimplificationPasses = void 0;
4
4
  exports.simplifyControlFlowInformation = simplifyControlFlowInformation;
5
- const simple_visitor_1 = require("./simple-visitor");
6
5
  const cfg_to_basic_blocks_1 = require("./cfg-to-basic-blocks");
7
- const CfgSimplificationPasses = {
6
+ const simple_visitor_1 = require("./simple-visitor");
7
+ const cfg_dead_code_1 = require("./cfg-dead-code");
8
+ exports.CfgSimplificationPasses = {
8
9
  'unique-cf-sets': uniqueControlFlowSets,
10
+ 'analyze-dead-code': cfg_dead_code_1.cfgAnalyzeDeadCode,
9
11
  'remove-dead-code': cfgRemoveDeadCode,
10
12
  'to-basic-blocks': toBasicBlocks
11
13
  };
@@ -18,25 +20,17 @@ exports.DefaultCfgSimplificationOrder = [
18
20
  * Simplify the control flow information by applying the given passes.
19
21
  * This may reduce the vertex count, in- and outgoing edges, entry and exit points, etc.
20
22
  */
21
- function simplifyControlFlowInformation(cfg, passes = exports.DefaultCfgSimplificationOrder) {
23
+ function simplifyControlFlowInformation(cfg, info, passes = exports.DefaultCfgSimplificationOrder) {
22
24
  for (const pass of passes) {
23
- const passFn = CfgSimplificationPasses[pass];
24
- cfg = passFn(cfg);
25
+ const passFn = exports.CfgSimplificationPasses[pass];
26
+ cfg = passFn(cfg, info);
25
27
  }
26
28
  return cfg;
27
29
  }
28
- function uniqueControlFlowSets(cfg) {
29
- return {
30
- returns: [...new Set(cfg.returns)],
31
- entryPoints: [...new Set(cfg.entryPoints)],
32
- exitPoints: [...new Set(cfg.exitPoints)],
33
- breaks: [...new Set(cfg.breaks)],
34
- nexts: [...new Set(cfg.nexts)],
35
- graph: cfg.graph
36
- };
37
- }
38
- /* currently this does not do work on function definitions */
39
- function cfgRemoveDeadCode(cfg) {
30
+ /**
31
+ * removes dead vertices and edges from the control flow graph.
32
+ */
33
+ function cfgRemoveDeadCode(cfg, _info) {
40
34
  // remove every root level node and accompanying vertices that can not be reached from the entry points
41
35
  const reachable = new Set();
42
36
  (0, simple_visitor_1.visitCfgInOrder)(cfg.graph, cfg.entryPoints, node => {
@@ -49,7 +43,17 @@ function cfgRemoveDeadCode(cfg) {
49
43
  }
50
44
  return cfg;
51
45
  }
52
- function toBasicBlocks(cfg) {
46
+ function uniqueControlFlowSets(cfg, _info) {
47
+ return {
48
+ returns: [...new Set(cfg.returns)],
49
+ entryPoints: [...new Set(cfg.entryPoints)],
50
+ exitPoints: [...new Set(cfg.exitPoints)],
51
+ breaks: [...new Set(cfg.breaks)],
52
+ nexts: [...new Set(cfg.nexts)],
53
+ graph: cfg.graph
54
+ };
55
+ }
56
+ function toBasicBlocks(cfg, _info) {
53
57
  return (0, cfg_to_basic_blocks_1.convertCfgToBasicBlocks)(cfg);
54
58
  }
55
59
  //# sourceMappingURL=cfg-simplification.js.map
@@ -70,7 +70,7 @@ export declare function equalVertex(a: CfgSimpleVertex, b: CfgSimpleVertex): boo
70
70
  interface CfgFlowDependencyEdge extends MergeableRecord {
71
71
  label: CfgEdgeType.Fd;
72
72
  }
73
- interface CfgControlDependencyEdge extends MergeableRecord {
73
+ export interface CfgControlDependencyEdge extends MergeableRecord {
74
74
  label: CfgEdgeType.Cd;
75
75
  /** the id which caused the control dependency */
76
76
  caused: NodeId;
@@ -166,6 +166,7 @@ export interface ReadOnlyControlFlowGraph {
166
166
  */
167
167
  export declare class ControlFlowGraph<Vertex extends CfgSimpleVertex = CfgSimpleVertex> implements ReadOnlyControlFlowGraph {
168
168
  private readonly rootVertices;
169
+ /** Nesting-Independent vertex information, mapping the id to the vertex */
169
170
  private readonly vertexInformation;
170
171
  /** the basic block children map contains a mapping of ids to all vertices that are nested in basic blocks, mapping them to the Id of the block they appear in */
171
172
  private readonly bbChildren;
@@ -55,6 +55,7 @@ function equalVertex(a, b) {
55
55
  */
56
56
  class ControlFlowGraph {
57
57
  rootVertices = new Set();
58
+ /** Nesting-Independent vertex information, mapping the id to the vertex */
58
59
  vertexInformation = new Map();
59
60
  /** the basic block children map contains a mapping of ids to all vertices that are nested in basic blocks, mapping them to the Id of the block they appear in */
60
61
  bbChildren = new Map();
@@ -1,18 +1,18 @@
1
1
  import type { CfgExpressionVertex, CfgStatementVertex, ControlFlowInformation } from './control-flow-graph';
2
2
  import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id';
3
- import type { DataflowInformation } from '../dataflow/info';
4
3
  import type { DataflowGraphVertexArgument, DataflowGraphVertexFunctionCall, DataflowGraphVertexFunctionDefinition, DataflowGraphVertexUse, DataflowGraphVertexValue, DataflowGraphVertexVariableDefinition } from '../dataflow/graph/vertex';
5
4
  import type { BasicCfgGuidedVisitorConfiguration } from './basic-cfg-guided-visitor';
6
5
  import { BasicCfgGuidedVisitor } from './basic-cfg-guided-visitor';
7
- export interface DataflowCfgGuidedVisitorConfiguration<Cfg extends ControlFlowInformation = ControlFlowInformation, Dfg extends DataflowInformation = DataflowInformation> extends BasicCfgGuidedVisitorConfiguration<Cfg> {
8
- readonly dataflow: Dfg;
6
+ import type { DataflowGraph } from '../dataflow/graph/graph';
7
+ export interface DataflowCfgGuidedVisitorConfiguration<ControlFlow extends ControlFlowInformation = ControlFlowInformation, Dfg extends DataflowGraph = DataflowGraph> extends BasicCfgGuidedVisitorConfiguration<ControlFlow> {
8
+ readonly dfg: Dfg;
9
9
  }
10
10
  /**
11
11
  * This visitor extends on the {@link BasicCfgGuidedVisitor} by dispatching visitors based on the dataflow graph.
12
12
  *
13
13
  * Use {@link BasicCfgGuidedVisitor#start} to start the traversal.
14
14
  */
15
- export declare class DataflowAwareCfgGuidedVisitor<Cfg extends ControlFlowInformation = ControlFlowInformation, Dfg extends DataflowInformation = DataflowInformation, Config extends DataflowCfgGuidedVisitorConfiguration<Cfg, Dfg> = DataflowCfgGuidedVisitorConfiguration<Cfg, Dfg>> extends BasicCfgGuidedVisitor<Cfg, Config> {
15
+ export declare class DataflowAwareCfgGuidedVisitor<ControlFlow extends ControlFlowInformation = ControlFlowInformation, Dfg extends DataflowGraph = DataflowGraph, Config extends DataflowCfgGuidedVisitorConfiguration<ControlFlow, Dfg> = DataflowCfgGuidedVisitorConfiguration<ControlFlow, Dfg>> extends BasicCfgGuidedVisitor<ControlFlow, Config> {
16
16
  /**
17
17
  * Get the dataflow graph vertex for the given id
18
18
  */
@@ -14,7 +14,7 @@ class DataflowAwareCfgGuidedVisitor extends basic_cfg_guided_visitor_1.BasicCfgG
14
14
  * Get the dataflow graph vertex for the given id
15
15
  */
16
16
  getDataflowGraph(id) {
17
- return this.config.dataflow.graph.getVertex(id);
17
+ return this.config.dfg.getVertex(id);
18
18
  }
19
19
  onStatementNode(node) {
20
20
  super.onStatementNode(node);
@@ -14,7 +14,7 @@ import type { CfgSimplificationPassName } from './cfg-simplification';
14
14
  *
15
15
  * @see {@link extractSimpleCfg} - for a simplified version of this function
16
16
  */
17
- export declare function extractCfg<Info = ParentInformation>(ast: NormalizedAst<Info>, graph?: DataflowGraph, simplifications?: readonly CfgSimplificationPassName[]): ControlFlowInformation;
17
+ export declare function extractCfg<Info = ParentInformation>(ast: NormalizedAst<Info & ParentInformation>, graph?: DataflowGraph, simplifications?: readonly CfgSimplificationPassName[]): ControlFlowInformation;
18
18
  /**
19
19
  * Simplified version of {@link extractCfg} that is much quicker, but much simpler!
20
20
  */
@@ -63,7 +63,7 @@ function dataflowCfgFolds(dataflowGraph) {
63
63
  * @see {@link extractSimpleCfg} - for a simplified version of this function
64
64
  */
65
65
  function extractCfg(ast, graph, simplifications) {
66
- return (0, cfg_simplification_1.simplifyControlFlowInformation)((0, fold_1.foldAst)(ast.ast, graph ? dataflowCfgFolds(graph) : cfgFolds), simplifications);
66
+ return (0, cfg_simplification_1.simplifyControlFlowInformation)((0, fold_1.foldAst)(ast.ast, graph ? dataflowCfgFolds(graph) : cfgFolds), { ast, dfg: graph }, simplifications);
67
67
  }
68
68
  /**
69
69
  * Simplified version of {@link extractCfg} that is much quicker, but much simpler!
@@ -72,10 +72,8 @@ function extractSimpleCfg(ast) {
72
72
  return (0, fold_1.foldAst)(ast.ast, cfgFolds);
73
73
  }
74
74
  function cfgLeaf(type) {
75
- return (leaf) => {
76
- const graph = new control_flow_graph_1.ControlFlowGraph();
77
- graph.addVertex({ id: leaf.info.id, type });
78
- return { graph, breaks: [], nexts: [], returns: [], exitPoints: [leaf.info.id], entryPoints: [leaf.info.id] };
75
+ return ({ info: { id } }) => {
76
+ return { graph: new control_flow_graph_1.ControlFlowGraph().addVertex({ id, type }), breaks: [], nexts: [], returns: [], exitPoints: [id], entryPoints: [id] };
79
77
  };
80
78
  }
81
79
  function cfgBreak(leaf) {
@@ -91,40 +89,41 @@ function identifyMayStatementType(node) {
91
89
  return node.info.role === "expr-list-child" /* RoleInParent.ExpressionListChild */ ? control_flow_graph_1.CfgVertexType.Statement : control_flow_graph_1.CfgVertexType.Expression;
92
90
  }
93
91
  function cfgIfThenElse(ifNode, condition, then, otherwise) {
92
+ const ifId = ifNode.info.id;
94
93
  const graph = new control_flow_graph_1.ControlFlowGraph();
95
- graph.addVertex({ id: ifNode.info.id, type: identifyMayStatementType(ifNode), mid: [ifNode.info.id + '-condition'], end: [ifNode.info.id + '-exit'] });
96
- graph.addVertex({ id: ifNode.info.id + '-condition', kind: 'condition', type: control_flow_graph_1.CfgVertexType.MidMarker, root: ifNode.info.id });
97
- graph.addVertex({ id: ifNode.info.id + '-exit', type: control_flow_graph_1.CfgVertexType.EndMarker, root: ifNode.info.id });
94
+ graph.addVertex({ id: ifId, type: identifyMayStatementType(ifNode), mid: [ifId + '-condition'], end: [ifId + '-exit'] });
95
+ graph.addVertex({ id: ifId + '-condition', kind: 'condition', type: control_flow_graph_1.CfgVertexType.MidMarker, root: ifId });
96
+ graph.addVertex({ id: ifId + '-exit', type: control_flow_graph_1.CfgVertexType.EndMarker, root: ifId });
98
97
  graph.mergeWith(condition.graph);
99
98
  graph.mergeWith(then.graph);
100
99
  if (otherwise) {
101
100
  graph.mergeWith(otherwise.graph);
102
101
  }
103
102
  for (const exitPoint of condition.exitPoints) {
104
- graph.addEdge(ifNode.info.id + '-condition', exitPoint, { label: 0 /* CfgEdgeType.Fd */ });
103
+ graph.addEdge(ifId + '-condition', exitPoint, { label: 0 /* CfgEdgeType.Fd */ });
105
104
  }
106
105
  for (const entryPoint of then.entryPoints) {
107
- graph.addEdge(entryPoint, ifNode.info.id + '-condition', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RTrue, caused: ifNode.info.id });
106
+ graph.addEdge(entryPoint, ifId + '-condition', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RTrue, caused: ifId });
108
107
  }
109
108
  for (const entryPoint of otherwise?.entryPoints ?? []) {
110
- graph.addEdge(entryPoint, ifNode.info.id + '-condition', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RFalse, caused: ifNode.info.id });
109
+ graph.addEdge(entryPoint, ifId + '-condition', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RFalse, caused: ifId });
111
110
  }
112
111
  for (const entryPoint of condition.entryPoints) {
113
- graph.addEdge(entryPoint, ifNode.info.id, { label: 0 /* CfgEdgeType.Fd */ });
112
+ graph.addEdge(entryPoint, ifId, { label: 0 /* CfgEdgeType.Fd */ });
114
113
  }
115
114
  for (const exit of [...then.exitPoints, ...otherwise?.exitPoints ?? []]) {
116
- graph.addEdge(ifNode.info.id + '-exit', exit, { label: 0 /* CfgEdgeType.Fd */ });
115
+ graph.addEdge(ifId + '-exit', exit, { label: 0 /* CfgEdgeType.Fd */ });
117
116
  }
118
117
  if (!otherwise) {
119
- graph.addEdge(ifNode.info.id + '-exit', ifNode.info.id + '-condition', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RFalse, caused: ifNode.info.id });
118
+ graph.addEdge(ifId + '-exit', ifId + '-condition', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RFalse, caused: ifId });
120
119
  }
121
120
  return {
122
121
  graph,
123
122
  breaks: [...then.breaks, ...otherwise?.breaks ?? []],
124
123
  nexts: [...then.nexts, ...otherwise?.nexts ?? []],
125
124
  returns: [...then.returns, ...otherwise?.returns ?? []],
126
- exitPoints: [ifNode.info.id + '-exit'],
127
- entryPoints: [ifNode.info.id]
125
+ exitPoints: [ifId + '-exit'],
126
+ entryPoints: [ifId]
128
127
  };
129
128
  }
130
129
  function cfgRepeat(repeat, body) {
@@ -144,110 +143,114 @@ function cfgRepeat(repeat, body) {
144
143
  return { graph, breaks: [], nexts: [], returns: body.returns, exitPoints: [repeat.info.id + '-exit'], entryPoints: [repeat.info.id] };
145
144
  }
146
145
  function cfgWhile(whileLoop, condition, body) {
146
+ const whileId = whileLoop.info.id;
147
147
  const graph = condition.graph;
148
- graph.addVertex({ id: whileLoop.info.id, type: identifyMayStatementType(whileLoop), mid: [whileLoop.info.id + '-condition'], end: [whileLoop.info.id + '-exit'] });
149
- graph.addVertex({ id: whileLoop.info.id + '-condition', kind: 'condition', type: control_flow_graph_1.CfgVertexType.MidMarker, root: whileLoop.info.id });
150
- graph.addVertex({ id: whileLoop.info.id + '-exit', type: control_flow_graph_1.CfgVertexType.EndMarker, root: whileLoop.info.id });
148
+ graph.addVertex({ id: whileId, type: identifyMayStatementType(whileLoop), mid: [whileId + '-condition'], end: [whileId + '-exit'] });
149
+ graph.addVertex({ id: whileId + '-condition', kind: 'condition', type: control_flow_graph_1.CfgVertexType.MidMarker, root: whileId });
150
+ graph.addVertex({ id: whileId + '-exit', type: control_flow_graph_1.CfgVertexType.EndMarker, root: whileId });
151
151
  graph.mergeWith(body.graph);
152
152
  for (const entry of condition.entryPoints) {
153
- graph.addEdge(entry, whileLoop.info.id, { label: 0 /* CfgEdgeType.Fd */ });
153
+ graph.addEdge(entry, whileId, { label: 0 /* CfgEdgeType.Fd */ });
154
154
  }
155
155
  for (const exit of condition.exitPoints) {
156
- graph.addEdge(whileLoop.info.id + '-condition', exit, { label: 0 /* CfgEdgeType.Fd */ });
156
+ graph.addEdge(whileId + '-condition', exit, { label: 0 /* CfgEdgeType.Fd */ });
157
157
  }
158
158
  for (const entry of body.entryPoints) {
159
- graph.addEdge(entry, whileLoop.info.id + '-condition', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RTrue, caused: whileLoop.info.id });
159
+ graph.addEdge(entry, whileId + '-condition', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RTrue, caused: whileId });
160
160
  }
161
161
  for (const next of [...body.nexts, ...body.exitPoints]) {
162
- graph.addEdge(whileLoop.info.id, next, { label: 0 /* CfgEdgeType.Fd */ });
162
+ graph.addEdge(whileId, next, { label: 0 /* CfgEdgeType.Fd */ });
163
163
  }
164
164
  for (const breakPoint of body.breaks) {
165
- graph.addEdge(whileLoop.info.id + '-exit', breakPoint, { label: 0 /* CfgEdgeType.Fd */ });
165
+ graph.addEdge(whileId + '-exit', breakPoint, { label: 0 /* CfgEdgeType.Fd */ });
166
166
  }
167
167
  // while can break on the condition as well
168
- graph.addEdge(whileLoop.info.id + '-exit', whileLoop.info.id + '-condition', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RFalse, caused: whileLoop.info.id });
169
- return { graph, breaks: [], nexts: [], returns: body.returns, exitPoints: [whileLoop.info.id + '-exit'], entryPoints: [whileLoop.info.id] };
168
+ graph.addEdge(whileId + '-exit', whileId + '-condition', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RFalse, caused: whileId });
169
+ return { graph, breaks: [], nexts: [], returns: body.returns, exitPoints: [whileId + '-exit'], entryPoints: [whileId] };
170
170
  }
171
171
  function cfgFor(forLoop, variable, vector, body) {
172
+ const forLoopId = forLoop.info.id;
172
173
  const graph = variable.graph;
173
- graph.addVertex({ id: forLoop.info.id, type: identifyMayStatementType(forLoop), end: [forLoop.info.id + '-exit'], mid: [forLoop.info.id + '-head'] });
174
+ graph.addVertex({ id: forLoopId, type: identifyMayStatementType(forLoop), end: [forLoopId + '-exit'], mid: [forLoopId + '-head'] });
174
175
  graph.mergeWith(vector.graph);
175
176
  graph.mergeWith(body.graph);
176
177
  for (const entry of vector.entryPoints) {
177
- graph.addEdge(entry, forLoop.info.id, { label: 0 /* CfgEdgeType.Fd */ });
178
+ graph.addEdge(entry, forLoopId, { label: 0 /* CfgEdgeType.Fd */ });
178
179
  }
179
180
  for (const exit of vector.exitPoints) {
180
181
  for (const entry of variable.entryPoints) {
181
182
  graph.addEdge(entry, exit, { label: 0 /* CfgEdgeType.Fd */ });
182
183
  }
183
184
  }
184
- graph.addVertex({ id: forLoop.info.id + '-head', type: control_flow_graph_1.CfgVertexType.MidMarker, root: forLoop.info.id, kind: 'head' });
185
+ graph.addVertex({ id: forLoopId + '-head', type: control_flow_graph_1.CfgVertexType.MidMarker, root: forLoopId, kind: 'head' });
185
186
  for (const exit of variable.exitPoints) {
186
- graph.addEdge(forLoop.info.id + '-head', exit, { label: 0 /* CfgEdgeType.Fd */ });
187
+ graph.addEdge(forLoopId + '-head', exit, { label: 0 /* CfgEdgeType.Fd */ });
187
188
  }
188
189
  for (const entry of body.entryPoints) {
189
- graph.addEdge(entry, forLoop.info.id + '-head', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RTrue, caused: forLoop.info.id });
190
+ graph.addEdge(entry, forLoopId + '-head', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RTrue, caused: forLoopId });
190
191
  }
191
192
  for (const next of [...body.nexts, ...body.exitPoints]) {
192
- graph.addEdge(forLoop.info.id, next, { label: 0 /* CfgEdgeType.Fd */ });
193
+ graph.addEdge(forLoopId, next, { label: 0 /* CfgEdgeType.Fd */ });
193
194
  }
194
195
  for (const breakPoint of body.breaks) {
195
- graph.addEdge(forLoop.info.id + '-exit', breakPoint, { label: 0 /* CfgEdgeType.Fd */ });
196
+ graph.addEdge(forLoopId + '-exit', breakPoint, { label: 0 /* CfgEdgeType.Fd */ });
196
197
  }
197
198
  const isNotEndless = body.exitPoints.length > 0 || body.breaks.length > 0;
198
199
  if (isNotEndless) {
199
200
  graph.addVertex({
200
- id: forLoop.info.id + '-exit',
201
+ id: forLoopId + '-exit',
201
202
  type: control_flow_graph_1.CfgVertexType.EndMarker,
202
- root: forLoop.info.id
203
+ root: forLoopId
203
204
  });
204
- graph.addEdge(forLoop.info.id + '-exit', forLoop.info.id + '-head', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RFalse, caused: forLoop.info.id });
205
+ graph.addEdge(forLoopId + '-exit', forLoopId + '-head', { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RFalse, caused: forLoopId });
205
206
  }
206
- return { graph, breaks: [], nexts: [], returns: body.returns, exitPoints: isNotEndless ? [forLoop.info.id + '-exit'] : [], entryPoints: [forLoop.info.id] };
207
+ return { graph, breaks: [], nexts: [], returns: body.returns, exitPoints: isNotEndless ? [forLoopId + '-exit'] : [], entryPoints: [forLoopId] };
207
208
  }
208
209
  function cfgFunctionDefinition(fn, params, body) {
210
+ const fnId = fn.info.id;
209
211
  const graph = new control_flow_graph_1.ControlFlowGraph();
210
- const children = [fn.info.id + '-params', fn.info.id + '-exit'];
211
- graph.addVertex({ id: fn.info.id + '-params', kind: 'parameters', type: control_flow_graph_1.CfgVertexType.MidMarker, root: fn.info.id }, false);
212
- graph.addVertex({ id: fn.info.id + '-exit', type: control_flow_graph_1.CfgVertexType.EndMarker, root: fn.info.id }, false);
213
- graph.addVertex({ id: fn.info.id, children, type: identifyMayStatementType(fn), mid: [fn.info.id + '-params'], end: [fn.info.id + '-exit'] });
212
+ const children = [fnId + '-params', fnId + '-exit'];
213
+ graph.addVertex({ id: fnId + '-params', kind: 'parameters', type: control_flow_graph_1.CfgVertexType.MidMarker, root: fnId }, false);
214
+ graph.addVertex({ id: fnId + '-exit', type: control_flow_graph_1.CfgVertexType.EndMarker, root: fnId }, false);
215
+ graph.addVertex({ id: fnId, children, type: identifyMayStatementType(fn), mid: [fnId + '-params'], end: [fnId + '-exit'] });
214
216
  graph.mergeWith(body.graph, true);
215
217
  children.push(...body.graph.rootIds());
216
218
  for (const param of params) {
217
219
  graph.mergeWith(param.graph, true);
218
220
  children.push(...param.graph.rootIds());
219
221
  for (const entry of param.entryPoints) {
220
- graph.addEdge(entry, fn.info.id, { label: 0 /* CfgEdgeType.Fd */ });
222
+ graph.addEdge(entry, fnId, { label: 0 /* CfgEdgeType.Fd */ });
221
223
  }
222
224
  for (const exit of param.exitPoints) {
223
- graph.addEdge(fn.info.id + '-params', exit, { label: 0 /* CfgEdgeType.Fd */ });
225
+ graph.addEdge(fnId + '-params', exit, { label: 0 /* CfgEdgeType.Fd */ });
224
226
  }
225
227
  }
226
228
  if (params.length === 0) {
227
- graph.addEdge(fn.info.id + '-params', fn.info.id, { label: 0 /* CfgEdgeType.Fd */ });
229
+ graph.addEdge(fnId + '-params', fnId, { label: 0 /* CfgEdgeType.Fd */ });
228
230
  }
229
231
  for (const entry of body.entryPoints) {
230
- graph.addEdge(entry, fn.info.id + '-params', { label: 0 /* CfgEdgeType.Fd */ });
232
+ graph.addEdge(entry, fnId + '-params', { label: 0 /* CfgEdgeType.Fd */ });
231
233
  }
232
- // breaks and nexts should be illegal but safe is safe i guess
233
- for (const next of [...body.returns, ...body.breaks, ...body.nexts, ...body.exitPoints]) {
234
- graph.addEdge(fn.info.id + '-exit', next, { label: 0 /* CfgEdgeType.Fd */ });
234
+ // breaks and nexts should be illegal but safe is safe, I guess
235
+ for (const next of body.returns.concat(body.breaks, body.nexts, body.exitPoints)) {
236
+ graph.addEdge(fnId + '-exit', next, { label: 0 /* CfgEdgeType.Fd */ });
235
237
  }
236
- return { graph: graph, breaks: [], nexts: [], returns: [], exitPoints: [fn.info.id], entryPoints: [fn.info.id] };
238
+ return { graph: graph, breaks: [], nexts: [], returns: [], exitPoints: [fnId], entryPoints: [fnId] };
237
239
  }
238
240
  function cfgFunctionCall(call, name, args, exit = 'exit') {
241
+ const callId = call.info.id;
239
242
  const graph = name.graph;
240
- const info = { graph, breaks: [...name.breaks], nexts: [...name.nexts], returns: [...name.returns], exitPoints: [call.info.id + '-' + exit], entryPoints: [call.info.id] };
241
- graph.addVertex({ id: call.info.id, type: identifyMayStatementType(call), mid: [call.info.id + '-name'], end: [call.info.id + '-' + exit] });
243
+ const info = { graph, breaks: [...name.breaks], nexts: [...name.nexts], returns: [...name.returns], exitPoints: [callId + '-' + exit], entryPoints: [callId] };
244
+ graph.addVertex({ id: callId, type: identifyMayStatementType(call), mid: [callId + '-name'], end: [callId + '-' + exit] });
242
245
  for (const entryPoint of name.entryPoints) {
243
- graph.addEdge(entryPoint, call.info.id, { label: 0 /* CfgEdgeType.Fd */ });
246
+ graph.addEdge(entryPoint, callId, { label: 0 /* CfgEdgeType.Fd */ });
244
247
  }
245
- graph.addVertex({ id: call.info.id + '-name', kind: 'name', type: control_flow_graph_1.CfgVertexType.MidMarker, root: call.info.id });
248
+ graph.addVertex({ id: callId + '-name', kind: 'name', type: control_flow_graph_1.CfgVertexType.MidMarker, root: callId });
246
249
  for (const exitPoint of name.exitPoints) {
247
- graph.addEdge(call.info.id + '-name', exitPoint, { label: 0 /* CfgEdgeType.Fd */ });
250
+ graph.addEdge(callId + '-name', exitPoint, { label: 0 /* CfgEdgeType.Fd */ });
248
251
  }
249
- graph.addVertex({ id: call.info.id + '-' + exit, type: control_flow_graph_1.CfgVertexType.EndMarker, root: call.info.id });
250
- let lastArgExits = [call.info.id + '-name'];
252
+ graph.addVertex({ id: callId + '-' + exit, type: control_flow_graph_1.CfgVertexType.EndMarker, root: callId });
253
+ let lastArgExits = [callId + '-name'];
251
254
  for (const arg of args) {
252
255
  if (arg === r_function_call_1.EmptyArgument) {
253
256
  continue;
@@ -264,7 +267,7 @@ function cfgFunctionCall(call, name, args, exit = 'exit') {
264
267
  lastArgExits = arg.exitPoints;
265
268
  }
266
269
  for (const exit of lastArgExits) {
267
- graph.addEdge(call.info.id + '-exit', exit, { label: 0 /* CfgEdgeType.Fd */ });
270
+ graph.addEdge(callId + '-exit', exit, { label: 0 /* CfgEdgeType.Fd */ });
268
271
  }
269
272
  // should not contain any breaks, nexts, or returns, (except for the body if something like 'break()')
270
273
  return info;