@eagleoutice/flowr 2.9.7 → 2.9.9

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 (40) hide show
  1. package/README.md +21 -21
  2. package/abstract-interpretation/absint-visitor.d.ts +5 -4
  3. package/abstract-interpretation/absint-visitor.js +12 -11
  4. package/control-flow/basic-cfg-guided-visitor.d.ts +3 -3
  5. package/control-flow/basic-cfg-guided-visitor.js +7 -6
  6. package/control-flow/cfg-dead-code.js +12 -9
  7. package/control-flow/cfg-properties.d.ts +2 -2
  8. package/control-flow/cfg-properties.js +3 -2
  9. package/control-flow/cfg-to-basic-blocks.js +9 -12
  10. package/control-flow/control-flow-graph.d.ts +382 -78
  11. package/control-flow/control-flow-graph.js +591 -106
  12. package/control-flow/dfg-cfg-guided-visitor.d.ts +4 -4
  13. package/control-flow/dfg-cfg-guided-visitor.js +3 -3
  14. package/control-flow/diff-cfg.js +35 -29
  15. package/control-flow/extract-cfg.d.ts +3 -3
  16. package/control-flow/extract-cfg.js +145 -125
  17. package/control-flow/happens-before.js +2 -1
  18. package/control-flow/semantic-cfg-guided-visitor.js +2 -1
  19. package/control-flow/simple-visitor.js +8 -6
  20. package/control-flow/syntax-cfg-guided-visitor.js +2 -1
  21. package/control-flow/useless-loop.js +2 -1
  22. package/documentation/wiki-cfg.js +21 -9
  23. package/documentation/wiki-mk/doc-maker.js +1 -1
  24. package/package.json +1 -1
  25. package/project/context/flowr-file.d.ts +5 -0
  26. package/project/context/flowr-file.js +7 -0
  27. package/project/plugins/file-plugins/files/flowr-description-file.d.ts +1 -0
  28. package/project/plugins/file-plugins/files/flowr-description-file.js +5 -0
  29. package/project/plugins/file-plugins/files/flowr-namespace-file.d.ts +4 -0
  30. package/project/plugins/file-plugins/files/flowr-namespace-file.js +8 -0
  31. package/project/plugins/file-plugins/files/flowr-news-file.d.ts +4 -0
  32. package/project/plugins/file-plugins/files/flowr-news-file.js +8 -0
  33. package/queries/catalog/control-flow-query/control-flow-query-format.js +1 -1
  34. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.d.ts +2 -1
  35. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +19 -19
  36. package/search/search-executor/search-generators.js +2 -2
  37. package/util/assert.d.ts +3 -3
  38. package/util/mermaid/cfg.d.ts +1 -7
  39. package/util/mermaid/cfg.js +37 -44
  40. package/util/version.js +1 -1
@@ -10,6 +10,7 @@ const vertex_1 = require("../dataflow/graph/vertex");
10
10
  const info_1 = require("../dataflow/info");
11
11
  const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
12
12
  const assert_1 = require("../util/assert");
13
+ const control_flow_graph_1 = require("./control-flow-graph");
13
14
  const semantic_cfg_guided_visitor_1 = require("./semantic-cfg-guided-visitor");
14
15
  exports.loopyFunctions = new Set([built_in_1.BuiltInProcName.ForLoop, built_in_1.BuiltInProcName.WhileLoop, built_in_1.BuiltInProcName.RepeatLoop]);
15
16
  /**
@@ -88,7 +89,7 @@ class CfgSingleIterationLoopDetector extends semantic_cfg_guided_visitor_1.Seman
88
89
  startVisitor(_) {
89
90
  const g = this.config.controlFlow.graph;
90
91
  const ingoing = (i) => g.ingoingEdges(i);
91
- const exits = new Set(g.getVertex(this.loopToCheck)?.end ?? []);
92
+ const exits = new Set(control_flow_graph_1.CfgVertex.getEnd(g.getVertex(this.loopToCheck)) ?? []);
92
93
  (0, assert_1.guard)(exits.size !== 0, "Can't find end of loop");
93
94
  const stack = [this.loopToCheck];
94
95
  while (stack.length > 0) {
@@ -149,6 +149,12 @@ For readability, we structure this wiki page into various segments:
149
149
  - [Sophisticated CFG Traversal](#cfg-traversal)
150
150
  - [Working With Exit Points](#cfg-exit-points)
151
151
 
152
+ ${(0, doc_structure_1.block)({
153
+ type: 'TIP',
154
+ content: `FlowR provides you with various helper objects to work with the CFG, such as ${ctx.link('CfgEdge', undefined, { type: 'variable' })} and ${ctx.link('CfgVertex', undefined, { type: 'variable' })},
155
+ which you can use to easily access the properties of the CFG and its vertices and edges.`
156
+ })}
157
+
152
158
  ${(0, doc_structure_1.section)('Initial Overview', 2, 'cfg-overview')}
153
159
 
154
160
  For now, let's look at a CFG for a program without any branching:
@@ -360,7 +366,13 @@ And again it should be noted that even though the example code is more complicat
360
366
  ${(0, doc_structure_1.section)('Working with the CFG', 2, 'cfg-working')}
361
367
 
362
368
  There is a plethora of functions that you can use the traverse the [normalized AST](${doc_files_1.FlowrWikiBaseRef}/Normalized-AST) and the [dataflow graph](${doc_files_1.FlowrWikiBaseRef}/Dataflow%20Graph).
363
- Similarly, flowR provides you with a set of utility functions and classes that you can use to interact with the control flow graph.
369
+ Similarly, flowR provides you with a set of utility functions and classes that you can use to interact with the control flow graph:
370
+
371
+ * ${ctx.link(simple_visitor_1.visitCfgInOrder)} and ${ctx.link(simple_visitor_1.visitCfgInReverseOrder)} for simple traversals
372
+ * ${ctx.link(basic_cfg_guided_visitor_1.BasicCfgGuidedVisitor)}, ${ctx.link(syntax_cfg_guided_visitor_1.SyntaxAwareCfgGuidedVisitor)}, ${ctx.link(dfg_cfg_guided_visitor_1.DataflowAwareCfgGuidedVisitor)}, and ${ctx.link(semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor)} for more sophisticated traversals
373
+ * ${ctx.link('CfgEdge', undefined, { type: 'variable' })} and ${ctx.link('CfgVertex', undefined, { type: 'variable' })} for easy access to the properties of the CFG and its vertices and edges
374
+ * ${ctx.link(cfg_properties_1.assertCfgSatisfiesProperties)} and ${ctx.link('CfgProperties')} to check for properties of the CFG
375
+ * ${ctx.link(diff_cfg_1.diffOfControlFlowGraphs)} to diff two CFGs
364
376
 
365
377
  ${(0, doc_structure_1.section)('Simple Traversal', 3, 'cfg-simple-traversal')}
366
378
 
@@ -515,12 +527,12 @@ But the control flow perspective gives you more! Given a simple addition like \`
515
527
  ${await (async function () {
516
528
  const cfg = await (0, doc_cfg_1.getCfg)(shell, 'x + 1');
517
529
  const [plusVertexId, plusVertex] = cfg.info.graph.vertices().entries().filter(([n]) => (0, node_id_1.recoverName)(n, cfg.ast.idMap) === '+').toArray()[0];
518
- (0, assert_1.guard)(plusVertex.type === control_flow_graph_1.CfgVertexType.Expression);
519
- const numOfExits = plusVertex.end?.length ?? 0;
520
- (0, assert_1.guard)(plusVertex.end && numOfExits === 1);
530
+ (0, assert_1.guard)(control_flow_graph_1.CfgVertex.isExpression(plusVertex));
531
+ const numOfExits = control_flow_graph_1.CfgVertex.getEnd(plusVertex)?.length ?? 0;
532
+ (0, assert_1.guard)(numOfExits === 1);
521
533
  return `${await (0, doc_cfg_1.printCfgCode)(shell, 'x + 1', { showCode: true, prefix: 'flowchart RL\n' })}
522
534
 
523
- Looking at the binary operation vertex for \`+\` (with id \`${plusVertexId}\`) we see that it is linked to a single exit ("end marker") point: \`${plusVertex.end[0]}\`.
535
+ Looking at the binary operation vertex for \`+\` (with id \`${plusVertexId}\`) we see that it is linked to a single exit ("end marker") point: \`${control_flow_graph_1.CfgVertex.getEnd(plusVertex)?.[0] ?? '??'}\`.
524
536
  Checking this vertex essentially reveals all exit points of the expression ‐ in this case, this simply refers to the operands of the addition.
525
537
  However, the idea transfers to more complex expressions as well...
526
538
  `;
@@ -530,12 +542,12 @@ ${(0, doc_structure_1.details)('Example: Exit Points for an if', await (async fu
530
542
  const expr = 'if(u) 3 else 2';
531
543
  const cfg = await (0, doc_cfg_1.getCfg)(shell, expr);
532
544
  const [ifVertexId, ifVertex] = [...cfg.info.graph.vertices()].filter(([n]) => (0, node_id_1.recoverName)(n, cfg.ast.idMap) === 'if')[0];
533
- (0, assert_1.guard)(ifVertex.type === control_flow_graph_1.CfgVertexType.Statement);
534
- const numOfExits = ifVertex.end?.length ?? 0;
535
- (0, assert_1.guard)(ifVertex.end && numOfExits === 1);
545
+ (0, assert_1.guard)(control_flow_graph_1.CfgVertex.isStatement(ifVertex));
546
+ const numOfExits = control_flow_graph_1.CfgVertex.getEnd(ifVertex)?.length ?? 0;
547
+ (0, assert_1.guard)(numOfExits === 1);
536
548
  return `${await (0, doc_cfg_1.printCfgCode)(shell, expr, { showCode: true, prefix: 'flowchart RL\n' })}
537
549
 
538
- Looking at the if vertex for (with id \`${ifVertexId}\`) we see that it is again linked to a single exit point: \`${ifVertex.end[0]}\`.
550
+ Looking at the if vertex for (with id \`${ifVertexId}\`) we see that it is again linked to a single exit point: \`${control_flow_graph_1.CfgVertex.getEnd(ifVertex)?.[0] ?? '??'}\`.
539
551
  Yet, now this exit vertex is linked to the two branches of the if statement (the \`then\` and \`else\` branch).
540
552
  `;
541
553
  })())}
@@ -11,7 +11,7 @@ const DefaultReplacementPatterns = [
11
11
  // eslint-disable-next-line no-irregular-whitespace -- we may produce it in output
12
12
  [/\d+(\.\d+)?( |\s*)?ms/g, ''],
13
13
  [/tmp[%A-Za-z0-9-]+/g, ''],
14
- [/"?(timing|searchTimeMs|processTimeMs|id|treeSitterId)"?:\s*\d+(\.\d)?,?/g, ''],
14
+ [/"?(timing|searchTimeMs|processTimeMs|id|tsId)"?:\s*\d+(\.\d)?,?/g, ''],
15
15
  [/"format":"compact".+/gmius, ''],
16
16
  [/%%\s*\d*-+/g, ''],
17
17
  [/"[rR]": "\d+\.\d+\.\d+.*?"/g, ''],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.9.7",
3
+ "version": "2.9.9",
4
4
  "description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "repository": {
@@ -92,6 +92,11 @@ export declare abstract class FlowrFile<Content extends StringableContent = Stri
92
92
  get roles(): readonly FileRole[] | undefined;
93
93
  path(): string;
94
94
  content(): Content;
95
+ /**
96
+ * Allows to overwrite the content cache.
97
+ * @protected
98
+ */
99
+ protected setContent(content: Content): void;
95
100
  protected abstract loadContent(): Content;
96
101
  assignRole(role: FileRole): void;
97
102
  /**
@@ -62,6 +62,13 @@ class FlowrFile {
62
62
  }
63
63
  return this.contentCache;
64
64
  }
65
+ /**
66
+ * Allows to overwrite the content cache.
67
+ * @protected
68
+ */
69
+ setContent(content) {
70
+ this.contentCache = content;
71
+ }
65
72
  assignRole(role) {
66
73
  if (this._roles === undefined) {
67
74
  this._roles = [role];
@@ -24,6 +24,7 @@ export declare class FlowrDescriptionFile extends FlowrFile<DeepReadonly<DCF>> {
24
24
  * @see {@link parseDCF} for details on the parsing logic.
25
25
  */
26
26
  protected loadContent(): DCF;
27
+ static fromDCF(dcf: DCF, path: string, roles?: FileRole[]): FlowrDescriptionFile;
27
28
  /**
28
29
  * Description file lifter, this does not re-create if already a description file
29
30
  */
@@ -34,6 +34,11 @@ class FlowrDescriptionFile extends flowr_file_1.FlowrFile {
34
34
  loadContent() {
35
35
  return parseDCF(this.wrapped);
36
36
  }
37
+ static fromDCF(dcf, path, roles) {
38
+ const file = new FlowrDescriptionFile(new flowr_file_1.FlowrTextFile(path, roles));
39
+ file.setContent(dcf);
40
+ return file;
41
+ }
37
42
  /**
38
43
  * Description file lifter, this does not re-create if already a description file
39
44
  */
@@ -32,6 +32,10 @@ export declare class FlowrNamespaceFile extends FlowrFile<NamespaceFormat> {
32
32
  * and handle role assignments.
33
33
  */
34
34
  constructor(file: FlowrFileProvider, ctx?: FlowrAnalyzerContext);
35
+ /**
36
+ * Creates a {@link FlowrNamespaceFile} from a given {@link NamespaceFormat}, path and optional roles. This is useful if you already have the namespace content parsed and want to create a namespace file instance without re-parsing.
37
+ */
38
+ static fromNamespaceFormat(fmt: NamespaceFormat, path: string, roles?: FileRole[]): FlowrNamespaceFile;
35
39
  /**
36
40
  * Loads and parses the content of the wrapped file in the {@link NamespaceFormat}.
37
41
  * @see {@link parseNamespaceSimple} for details on the parsing logic.
@@ -30,6 +30,14 @@ class FlowrNamespaceFile extends flowr_file_1.FlowrFile {
30
30
  this.wrapped = file;
31
31
  this.ctx = ctx;
32
32
  }
33
+ /**
34
+ * Creates a {@link FlowrNamespaceFile} from a given {@link NamespaceFormat}, path and optional roles. This is useful if you already have the namespace content parsed and want to create a namespace file instance without re-parsing.
35
+ */
36
+ static fromNamespaceFormat(fmt, path, roles) {
37
+ const file = new FlowrNamespaceFile(new flowr_file_1.FlowrTextFile(path, roles));
38
+ file.setContent(fmt);
39
+ return file;
40
+ }
33
41
  /**
34
42
  * Loads and parses the content of the wrapped file in the {@link NamespaceFormat}.
35
43
  * @see {@link parseNamespaceSimple} for details on the parsing logic.
@@ -24,4 +24,8 @@ export declare class FlowrNewsFile extends FlowrFile<NewsChunk[]> {
24
24
  * News file lifter, this does not re-create if already a news file
25
25
  */
26
26
  static from(file: FlowrFileProvider | FlowrNewsFile, role?: FileRole): FlowrNewsFile;
27
+ /**
28
+ * Creates a FlowrNewsFile from given news chunks, path and optional roles. This is useful if you already have the news content parsed and want to create a news file instance without re-parsing.
29
+ */
30
+ static fromNewsChunks(chunks: NewsChunk[], path: string, roles?: FileRole[]): FlowrNewsFile;
27
31
  }
@@ -33,6 +33,14 @@ class FlowrNewsFile extends flowr_file_1.FlowrFile {
33
33
  }
34
34
  return file instanceof FlowrNewsFile ? file : new FlowrNewsFile(file);
35
35
  }
36
+ /**
37
+ * Creates a FlowrNewsFile from given news chunks, path and optional roles. This is useful if you already have the news content parsed and want to create a news file instance without re-parsing.
38
+ */
39
+ static fromNewsChunks(chunks, path, roles) {
40
+ const file = new FlowrNewsFile(new flowr_file_1.FlowrTextFile(path, roles));
41
+ file.setContent(chunks);
42
+ return file;
43
+ }
36
44
  }
37
45
  exports.FlowrNewsFile = FlowrNewsFile;
38
46
  function makeRversionRegex() {
@@ -27,7 +27,7 @@ exports.ControlFlowQueryDefinition = {
27
27
  flattenInvolvedNodes: (queryResults) => {
28
28
  const out = queryResults;
29
29
  return out.controlFlow.graph.vertices(true).entries()
30
- .filter(([, v]) => v.type !== control_flow_graph_1.CfgVertexType.Block)
30
+ .filter(([, v]) => !control_flow_graph_1.CfgVertex.isBlock(v))
31
31
  .map(v => v[0])
32
32
  .toArray();
33
33
  }
@@ -2,7 +2,8 @@ import type { Tree } from 'web-tree-sitter';
2
2
  import type { RProject } from '../ast/model/nodes/r-project';
3
3
  import type { ParseStepOutputSingleFile } from '../../parser';
4
4
  export interface TreeSitterInfo {
5
- treeSitterId: number;
5
+ /** id of the tree-sitter node this AST node was created from, used for traceability and linking back to tree-sitter nodes */
6
+ tsId: number;
6
7
  }
7
8
  /**
8
9
  * @param tree - The tree to normalize
@@ -77,7 +77,7 @@ function convertTreeNode(node) {
77
77
  info: {
78
78
  fullRange: undefined,
79
79
  adToks: [],
80
- treeSitterId: -1,
80
+ tsId: -1,
81
81
  }
82
82
  };
83
83
  }
@@ -90,7 +90,7 @@ function convertTreeNode(node) {
90
90
  fullRange: range,
91
91
  adToks: [],
92
92
  fullLexeme: node.text,
93
- treeSitterId: node.id
93
+ tsId: node.id
94
94
  }
95
95
  };
96
96
  switch (node.type) {
@@ -105,7 +105,7 @@ function convertTreeNode(node) {
105
105
  lexeme: undefined,
106
106
  info: {
107
107
  adToks: remainingComments.map(c => c[1]),
108
- treeSitterId: node.id
108
+ tsId: node.id
109
109
  }
110
110
  };
111
111
  }
@@ -138,7 +138,7 @@ function convertTreeNode(node) {
138
138
  ],
139
139
  info: {
140
140
  adToks: remainingComments.map(c => c[1]),
141
- treeSitterId: node.id
141
+ tsId: node.id
142
142
  }
143
143
  };
144
144
  }
@@ -156,7 +156,7 @@ function convertTreeNode(node) {
156
156
  name: undefined,
157
157
  lexeme: lhs.lexeme,
158
158
  info: {
159
- treeSitterId: lhs.info.treeSitterId
159
+ tsId: lhs.info.tsId
160
160
  }
161
161
  };
162
162
  if (op.type === 'special') {
@@ -170,7 +170,7 @@ function convertTreeNode(node) {
170
170
  lexeme: op.text,
171
171
  content: op.text,
172
172
  info: {
173
- treeSitterId: op.id
173
+ tsId: op.id
174
174
  }
175
175
  },
176
176
  arguments: [lhsAsArg, {
@@ -180,14 +180,14 @@ function convertTreeNode(node) {
180
180
  name: undefined,
181
181
  lexeme: rhs.lexeme,
182
182
  info: {
183
- treeSitterId: rhs.info.treeSitterId
183
+ tsId: rhs.info.tsId
184
184
  }
185
185
  }],
186
186
  named: true,
187
187
  infixSpecial: true,
188
188
  info: {
189
189
  adToks: comments,
190
- treeSitterId: node.id
190
+ tsId: node.id
191
191
  }
192
192
  };
193
193
  }
@@ -203,7 +203,7 @@ function convertTreeNode(node) {
203
203
  fullRange: range,
204
204
  adToks: comments,
205
205
  fullLexeme: node.text,
206
- treeSitterId: node.id
206
+ tsId: node.id
207
207
  }
208
208
  };
209
209
  }
@@ -218,7 +218,7 @@ function convertTreeNode(node) {
218
218
  fullRange: range,
219
219
  adToks: comments,
220
220
  fullLexeme: node.text,
221
- treeSitterId: node.id
221
+ tsId: node.id
222
222
  }
223
223
  };
224
224
  }
@@ -291,7 +291,7 @@ function convertTreeNode(node) {
291
291
  fullRange: undefined,
292
292
  adToks: [],
293
293
  fullLexeme: undefined,
294
- treeSitterId: variableNode.id
294
+ tsId: variableNode.id
295
295
  }
296
296
  },
297
297
  vector: convertTreeNode(sequenceNode),
@@ -302,7 +302,7 @@ function convertTreeNode(node) {
302
302
  fullRange: range,
303
303
  adToks: variableComments.concat(sequenceComments).map(c => c[1]),
304
304
  fullLexeme: node.text,
305
- treeSitterId: node.id
305
+ tsId: node.id
306
306
  }
307
307
  };
308
308
  }
@@ -369,7 +369,7 @@ function convertTreeNode(node) {
369
369
  fullRange: range,
370
370
  adToks: [],
371
371
  fullLexeme: node.text,
372
- treeSitterId: node.id
372
+ tsId: node.id
373
373
  }
374
374
  },
375
375
  named: true
@@ -480,7 +480,7 @@ function convertTreeNode(node) {
480
480
  fullRange: rhsRange,
481
481
  adToks: [],
482
482
  fullLexeme: rhs?.text,
483
- treeSitterId: rhs?.id
483
+ tsId: rhs?.id
484
484
  }
485
485
  }],
486
486
  location: makeSourceRange(operator),
@@ -507,7 +507,7 @@ function convertTreeNode(node) {
507
507
  fullRange: range,
508
508
  adToks: [],
509
509
  fullLexeme: name.text,
510
- treeSitterId: name.id
510
+ tsId: name.id
511
511
  }
512
512
  },
513
513
  special: name.text === '...',
@@ -518,7 +518,7 @@ function convertTreeNode(node) {
518
518
  fullRange: range,
519
519
  adToks: [],
520
520
  fullLexeme: name.text,
521
- treeSitterId: name.id
521
+ tsId: name.id
522
522
  }
523
523
  };
524
524
  }
@@ -560,7 +560,7 @@ function convertTreeNode(node) {
560
560
  fullRange: nameRange,
561
561
  adToks: [],
562
562
  fullLexeme: nameNode.text,
563
- treeSitterId: nameNode.id
563
+ tsId: nameNode.id
564
564
  }
565
565
  };
566
566
  }
@@ -595,7 +595,7 @@ function convertTreeNode(node) {
595
595
  info: {
596
596
  fullRange: undefined,
597
597
  adToks: [],
598
- treeSitterId: -1,
598
+ tsId: -1,
599
599
  }
600
600
  };
601
601
  }
@@ -624,7 +624,7 @@ function splitComments(nodes) {
624
624
  info: {
625
625
  adToks: [],
626
626
  fullLexeme: node.text,
627
- treeSitterId: node.id
627
+ tsId: node.id
628
628
  }
629
629
  }]);
630
630
  }
@@ -112,8 +112,8 @@ async function generateSyntax(input, args) {
112
112
  const nodesByTreeSitterId = new Map();
113
113
  (0, visitor_1.visitAst)((await input.normalize()).ast.files.map(f => f.root), node => {
114
114
  const treeSitterInfo = node.info;
115
- if (treeSitterInfo.treeSitterId) {
116
- nodesByTreeSitterId.set(treeSitterInfo.treeSitterId, node);
115
+ if (treeSitterInfo.tsId) {
116
+ nodesByTreeSitterId.set(treeSitterInfo.tsId, node);
117
117
  }
118
118
  else {
119
119
  exports.searchLogger.debug(`normalized ast node ${node.lexeme} with type ${node.type} does not have a tree-sitter id`);
package/util/assert.d.ts CHANGED
@@ -26,7 +26,7 @@ export declare function assertUnreachable(x: never): never;
26
26
  * @see {@link isUndefined}
27
27
  * @see {@link isNotNull}
28
28
  */
29
- export declare function isNotUndefined<T>(x: T | undefined): x is T;
29
+ export declare function isNotUndefined<T>(this: void, x: T | undefined): x is T;
30
30
  /**
31
31
  * Verifies that the given value is undefined.
32
32
  * This especially helps with a `.filter`
@@ -39,7 +39,7 @@ export declare function isNotUndefined<T>(x: T | undefined): x is T;
39
39
  * @see {@link isNotUndefined}
40
40
  * @see {@link isNotNull}
41
41
  */
42
- export declare function isUndefined<T>(x: T | undefined): x is undefined;
42
+ export declare function isUndefined<T>(this: void, x: T | undefined): x is undefined;
43
43
  /**
44
44
  * Verifies that the given value is not null.
45
45
  * This especially helps with a `.filter`
@@ -52,7 +52,7 @@ export declare function isUndefined<T>(x: T | undefined): x is undefined;
52
52
  * @see {@link isUndefined}
53
53
  * @see {@link isNotUndefined}
54
54
  */
55
- export declare function isNotNull<T>(x: T | null): x is T;
55
+ export declare function isNotNull<T>(this: void, x: T | null): x is T;
56
56
  /**
57
57
  * Generates a GitHub issue URL for reporting guard errors
58
58
  */
@@ -15,13 +15,7 @@ export declare const MermaidEntryPointDefaultMarkStyle: MermaidMarkStyle['vertex
15
15
  export declare const MermaidExitPointDefaultMarkStyle: MermaidMarkStyle['vertex'];
16
16
  /**
17
17
  * Convert the control flow graph to a mermaid string.
18
- * @param cfg - The control flow graph to convert.
19
- * @param normalizedAst - The normalized AST to use for the vertex content.
20
- * @param prefix - The prefix to use for the mermaid string.
21
- * @param simplify - Whether to simplify the control flow graph (especially in the context of basic blocks).
22
- * @param markStyle - The style to use for marked vertices and edges.
23
- * @param includeOnlyIds - If provided, only include the vertices with the given IDs.
24
- * @param mark - If provided, mark the given vertices and edges.
18
+ * @see {@link MermaidCfgGraphPrinterInfo} for additional options.
25
19
  */
26
20
  export declare function cfgToMermaid(cfg: ControlFlowInformation, normalizedAst: NormalizedAst, { prefix, simplify, markStyle, entryPointStyle, exitPointStyle, includeOnlyIds, mark, includeBasicBlockLabel, basicBlockCharacterLimit }?: MermaidCfgGraphPrinterInfo): string;
27
21
  /**
@@ -4,7 +4,6 @@ exports.MermaidExitPointDefaultMarkStyle = exports.MermaidEntryPointDefaultMarkS
4
4
  exports.cfgToMermaid = cfgToMermaid;
5
5
  exports.cfgToMermaidUrl = cfgToMermaidUrl;
6
6
  const mermaid_1 = require("./mermaid");
7
- const type_1 = require("../../r-bridge/lang-4.x/ast/model/type");
8
7
  const control_flow_graph_1 = require("../../control-flow/control-flow-graph");
9
8
  const reconstruct_1 = require("../../reconstruct/reconstruct");
10
9
  const auto_select_defaults_1 = require("../../reconstruct/auto-select/auto-select-defaults");
@@ -17,9 +16,9 @@ function getLexeme(n) {
17
16
  }
18
17
  function cfgOfNode(vert, normalizedVertex, id, content, output) {
19
18
  if (normalizedVertex && content !== undefined) {
20
- const start = vert.type === control_flow_graph_1.CfgVertexType.Expression ? '([' : '[';
21
- const end = vert.type === control_flow_graph_1.CfgVertexType.Expression ? '])' : ']';
22
- const name = `"\`${(0, mermaid_1.escapeMarkdown)(normalizedVertex.type)} (${id})${content ? '\n' + (0, mermaid_1.escapeMarkdown)(JSON.stringify(content)) : ''}${vert.callTargets ? '\n calls:' + (0, mermaid_1.escapeMarkdown)(JSON.stringify([...vert.callTargets])) : ''}\`"`;
19
+ const start = control_flow_graph_1.CfgVertex.isExpression(vert) ? '([' : '[';
20
+ const end = control_flow_graph_1.CfgVertex.isExpression(vert) ? '])' : ']';
21
+ const name = `"\`${(0, mermaid_1.escapeMarkdown)(normalizedVertex.type)} (${id})${content ? '\n' + (0, mermaid_1.escapeMarkdown)(JSON.stringify(content)) : ''}${control_flow_graph_1.CfgVertex.getCallTargets(vert) ? '\n calls:' + (0, mermaid_1.escapeMarkdown)(JSON.stringify([...control_flow_graph_1.CfgVertex.getCallTargets(vert)])) : ''}\`"`;
23
22
  output += ` n${id}${start}${name}${end}\n`;
24
23
  }
25
24
  else {
@@ -28,27 +27,25 @@ function cfgOfNode(vert, normalizedVertex, id, content, output) {
28
27
  return output;
29
28
  }
30
29
  const getDirRegex = /flowchart\s+([A-Za-z]+)/;
31
- function shouldIncludeNode(simplify, element, include) {
30
+ function shouldIncludeNode(simplify, v, include) {
32
31
  if (simplify) {
33
32
  // Only basic blocks are shown, so include the BB, if at least one child is selected
34
- return element.type == control_flow_graph_1.CfgVertexType.Block && element.elems.filter(elem => elem.type !== control_flow_graph_1.CfgVertexType.EndMarker).some(elem => include.has(elem.id));
33
+ return control_flow_graph_1.CfgVertex.isBlock(v) && control_flow_graph_1.CfgVertex.getBasicBlockElements(v)
34
+ .filter(elem => !control_flow_graph_1.CfgVertex.isMarker(elem))
35
+ .some(elem => include.has(control_flow_graph_1.CfgVertex.getId(elem)));
35
36
  }
36
37
  else {
37
38
  // Basic blocks and vertices are shown, include the BB, if all children are highlighted
38
- return element.type == control_flow_graph_1.CfgVertexType.Block
39
- ? element.elems.filter(elem => elem.type !== control_flow_graph_1.CfgVertexType.EndMarker).every(elem => include.has(elem.id))
40
- : include.has(element.id);
39
+ return control_flow_graph_1.CfgVertex.isBlock(v)
40
+ ? control_flow_graph_1.CfgVertex.getBasicBlockElements(v)
41
+ .filter(elem => !control_flow_graph_1.CfgVertex.isMarker(elem))
42
+ .every(elem => include.has(control_flow_graph_1.CfgVertex.getId(elem)))
43
+ : include.has(control_flow_graph_1.CfgVertex.getId(v));
41
44
  }
42
45
  }
43
46
  /**
44
47
  * Convert the control flow graph to a mermaid string.
45
- * @param cfg - The control flow graph to convert.
46
- * @param normalizedAst - The normalized AST to use for the vertex content.
47
- * @param prefix - The prefix to use for the mermaid string.
48
- * @param simplify - Whether to simplify the control flow graph (especially in the context of basic blocks).
49
- * @param markStyle - The style to use for marked vertices and edges.
50
- * @param includeOnlyIds - If provided, only include the vertices with the given IDs.
51
- * @param mark - If provided, mark the given vertices and edges.
48
+ * @see {@link MermaidCfgGraphPrinterInfo} for additional options.
52
49
  */
53
50
  function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify = false, markStyle = info_1.MermaidDefaultMarkStyle, entryPointStyle = exports.MermaidEntryPointDefaultMarkStyle, exitPointStyle = exports.MermaidExitPointDefaultMarkStyle, includeOnlyIds, mark, includeBasicBlockLabel = true, basicBlockCharacterLimit = 100 } = {}) {
54
51
  const hasBbandSimplify = simplify && cfg.graph.mayHaveBasicBlocks();
@@ -68,7 +65,7 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
68
65
  }
69
66
  // if we have a filter, we automatically add all vertices in the cfg that are *markers* for these ids and
70
67
  for (const [id, v] of cfg.graph.vertices()) {
71
- if (v.type === control_flow_graph_1.CfgVertexType.EndMarker && completed.has(v.root)) {
68
+ if (control_flow_graph_1.CfgVertex.isMarker(v) && completed.has(control_flow_graph_1.CfgVertex.unpackRootId(v))) {
72
69
  completed.add(id);
73
70
  }
74
71
  }
@@ -79,53 +76,48 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
79
76
  for (const [id, vertex] of cfg.graph.vertices(false)) {
80
77
  const normalizedVertex = normalizedAst?.idMap.get(id);
81
78
  const content = getLexeme(normalizedVertex);
82
- if (vertex.name === type_1.RType.ExpressionList && vertex.type === control_flow_graph_1.CfgVertexType.Expression && cfg.graph.hasVertex(id + '-exit')) {
83
- output += ` subgraph ${type_1.RType.ExpressionList} ${normalizedVertex?.info.fullLexeme ?? id}\n`;
84
- output += ` direction ${dirIs}\n`;
85
- }
86
- if (vertex.type === control_flow_graph_1.CfgVertexType.Block) {
79
+ if (control_flow_graph_1.CfgVertex.isBlock(vertex)) {
80
+ const elems = control_flow_graph_1.CfgVertex.getBasicBlockElements(vertex);
87
81
  if (simplify) {
88
- if (includeOnlyIds && !vertex.elems.some(elem => includeOnlyIds.has(elem.id))) {
82
+ if (includeOnlyIds && !elems.some(elem => includeOnlyIds.has(control_flow_graph_1.CfgVertex.getId(elem)))) {
89
83
  continue;
90
84
  }
91
- const ids = vertex.elems?.map(e => e.id) ?? [];
85
+ const ids = elems?.map(control_flow_graph_1.CfgVertex.getId) ?? [];
92
86
  const reconstruct = limitTo((0, reconstruct_1.reconstructToCode)(normalizedAst, { nodes: new Set(ids) }, auto_select_defaults_1.doNotAutoSelect).code, basicBlockCharacterLimit);
93
87
  const name = `"\`${includeBasicBlockLabel ? `Basic Block (${id})\n` : ''}${(0, mermaid_1.escapeMarkdown)(reconstruct)}\`"`;
94
88
  output += ` n${id}[[${name}]]\n`;
95
- diagramIncludedIds.add(vertex.id);
89
+ diagramIncludedIds.add(control_flow_graph_1.CfgVertex.getId(vertex));
96
90
  }
97
91
  else {
98
- if (includeOnlyIds && !vertex.elems.some(elem => includeOnlyIds.has(elem.id))) {
92
+ if (includeOnlyIds && !elems.some(elem => includeOnlyIds.has(control_flow_graph_1.CfgVertex.getId(elem)))) {
99
93
  continue;
100
94
  }
101
- output += ` subgraph n${vertex.id} [Block ${normalizedVertex?.info.fullLexeme ?? id}]\n`;
95
+ output += ` subgraph n${id} [Block ${normalizedVertex?.info.fullLexeme ?? id}]\n`;
102
96
  output += ` direction ${dirIs}\n`;
103
- diagramIncludedIds.add(vertex.id);
97
+ diagramIncludedIds.add(id);
104
98
  let last = undefined;
105
- for (const element of vertex.elems ?? []) {
106
- if (includeOnlyIds && !includeOnlyIds.has(element.id)) {
99
+ for (const element of elems ?? []) {
100
+ if (includeOnlyIds && !includeOnlyIds.has(control_flow_graph_1.CfgVertex.getId(element))) {
107
101
  last = undefined;
108
102
  continue;
109
103
  }
110
- const childNormalizedVertex = normalizedAst?.idMap.get(element.id);
104
+ const eid = control_flow_graph_1.CfgVertex.getId(element);
105
+ const childNormalizedVertex = normalizedAst?.idMap.get(eid);
111
106
  const childContent = getLexeme(childNormalizedVertex);
112
- output = cfgOfNode(vertex, childNormalizedVertex, element.id, childContent, output);
113
- diagramIncludedIds.add(element.id);
107
+ output = cfgOfNode(vertex, childNormalizedVertex, eid, childContent, output);
108
+ diagramIncludedIds.add(eid);
114
109
  // just to keep the order
115
110
  if (last) {
116
- output += ` ${last} -.-> n${element.id}\n`;
111
+ output += ` ${last} -.-> n${eid}\n`;
117
112
  }
118
- last = `n${element.id}`;
113
+ last = `n${eid}`;
119
114
  }
120
115
  output += ' end\n';
121
116
  }
122
117
  }
123
118
  else if (!includeOnlyIds || includeOnlyIds.has(id)) {
124
119
  output = cfgOfNode(vertex, normalizedVertex, id, content, output);
125
- diagramIncludedIds.add(vertex.id);
126
- }
127
- if (vertex.name === type_1.RType.ExpressionList && vertex.type === control_flow_graph_1.CfgVertexType.EndMarker) {
128
- output += ' end\n';
120
+ diagramIncludedIds.add(id);
129
121
  }
130
122
  }
131
123
  for (const [from, targets] of cfg.graph.edges()) {
@@ -136,9 +128,10 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
136
128
  if (!diagramIncludedIds.has(to)) {
137
129
  continue;
138
130
  }
139
- const edgeType = edge.label === 1 /* CfgEdgeType.Cd */ ? '-->' : '-.->';
140
- const edgeSuffix = edge.label === 1 /* CfgEdgeType.Cd */ ? ` (${edge.when})` : '';
141
- output += ` n${from} ${edgeType}|"${(0, mermaid_1.escapeMarkdown)((0, control_flow_graph_1.edgeTypeToString)(edge.label))}${edgeSuffix}"| n${to}\n`;
131
+ const isCd = control_flow_graph_1.CfgEdge.isControlDependency(edge);
132
+ const edgeType = isCd ? '-->' : '-.->';
133
+ const edgeSuffix = isCd ? ` (${control_flow_graph_1.CfgEdge.unpackWhen(edge)})` : '';
134
+ output += ` n${from} ${edgeType}|"${(0, mermaid_1.escapeMarkdown)(control_flow_graph_1.CfgEdge.typeToString(edge))}${edgeSuffix}"| n${to}\n`;
142
135
  }
143
136
  }
144
137
  for (const entryPoint of cfg.entryPoints) {
@@ -152,9 +145,9 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
152
145
  }
153
146
  }
154
147
  if (mark) {
155
- for (const [_, vertex] of cfg.graph.vertices(true)) {
148
+ for (const [id, vertex] of cfg.graph.vertices(true)) {
156
149
  if (shouldIncludeNode(hasBbandSimplify, vertex, mark)) {
157
- output += ` style n${vertex.id} ${markStyle.vertex}`;
150
+ output += ` style n${id} ${markStyle.vertex}`;
158
151
  }
159
152
  }
160
153
  }
package/util/version.js CHANGED
@@ -6,7 +6,7 @@ exports.printVersionInformation = printVersionInformation;
6
6
  const semver_1 = require("semver");
7
7
  const assert_1 = require("./assert");
8
8
  // this is automatically replaced with the current version by release-it
9
- const version = '2.9.7';
9
+ const version = '2.9.9';
10
10
  /**
11
11
  * Retrieves the current flowR version as a new {@link SemVer} object.
12
12
  */