@eagleoutice/flowr 2.9.5 → 2.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -30
- package/abstract-interpretation/data-frame/dataframe-domain.js +1 -2
- package/benchmark/slicer.d.ts +0 -1
- package/benchmark/slicer.js +3 -9
- package/benchmark/stats/print.js +1 -0
- package/benchmark/summarizer/data.d.ts +2 -0
- package/benchmark/summarizer/first-phase/process.js +3 -1
- package/benchmark/summarizer/second-phase/process.js +4 -0
- package/cli/repl/commands/repl-commands.d.ts +1 -0
- package/cli/repl/commands/repl-commands.js +1 -0
- package/cli/repl/commands/repl-normalize.d.ts +1 -0
- package/cli/repl/commands/repl-normalize.js +28 -1
- package/control-flow/basic-cfg-guided-visitor.js +1 -3
- package/control-flow/cfg-dead-code.js +15 -10
- package/control-flow/control-flow-graph.d.ts +3 -1
- package/control-flow/control-flow-graph.js +26 -19
- package/control-flow/extract-cfg.d.ts +2 -1
- package/control-flow/extract-cfg.js +11 -2
- package/control-flow/simple-visitor.d.ts +1 -2
- package/control-flow/simple-visitor.js +16 -13
- package/dataflow/fn/recursive-function.js +1 -1
- package/documentation/doc-readme.js +11 -9
- package/documentation/wiki-absint.js +4 -3
- package/documentation/wiki-mk/doc-context.d.ts +3 -0
- package/documentation/wiki-mk/doc-context.js +4 -1
- package/linter/linter-rules.d.ts +2 -2
- package/linter/rules/dataframe-access-validation.d.ts +2 -2
- package/linter/rules/dataframe-access-validation.js +3 -3
- package/package.json +1 -1
- package/project/cache/flowr-analyzer-controlflow-cache.js +3 -0
- package/project/cfg-kind.d.ts +5 -1
- package/project/cfg-kind.js +5 -1
- package/project/plugins/file-plugins/files/flowr-namespace-file.js +3 -2
- package/project/plugins/file-plugins/files/flowr-rmarkdown-file.d.ts +3 -0
- package/project/plugins/file-plugins/files/flowr-rmarkdown-file.js +13 -2
- package/project/plugins/file-plugins/files/flowr-sweave-file.d.ts +70 -0
- package/project/plugins/file-plugins/files/flowr-sweave-file.js +163 -0
- package/project/plugins/file-plugins/notebooks/flowr-analyzer-sweave-file-plugin.d.ts +22 -0
- package/project/plugins/file-plugins/notebooks/flowr-analyzer-sweave-file-plugin.js +33 -0
- package/project/plugins/flowr-analyzer-plugin-defaults.js +2 -0
- package/project/plugins/plugin-registry.d.ts +2 -1
- package/project/plugins/plugin-registry.js +2 -0
- package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +1 -1
- package/queries/catalog/call-context-query/call-context-query-executor.d.ts +3 -2
- package/queries/catalog/call-context-query/call-context-query-executor.js +16 -11
- package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +1 -1
- package/queries/catalog/call-context-query/identify-link-to-nested-call-relation.js +7 -4
- package/queries/catalog/dependencies-query/dependencies-query-executor.js +99 -81
- package/queries/catalog/df-shape-query/df-shape-query-executor.js +12 -5
- package/queries/query.js +4 -3
- package/r-bridge/data/data.d.ts +20 -0
- package/r-bridge/data/data.js +24 -0
- package/r-bridge/lang-4.x/convert-values.js +1 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +64 -34
- package/util/r-regex.d.ts +4 -0
- package/util/r-regex.js +35 -3
- package/util/version.js +1 -1
- package/control-flow/invert-cfg.d.ts +0 -5
- package/control-flow/invert-cfg.js +0 -20
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ It offers a wide variety of features, for example:
|
|
|
24
24
|
|
|
25
25
|
```shell
|
|
26
26
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
27
|
-
flowR repl using flowR v2.9.
|
|
27
|
+
flowR repl using flowR v2.9.6, R grammar v14 (tree-sitter engine)
|
|
28
28
|
R> :query @linter "read.csv(\"/root/x.txt\")"
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -33,21 +33,21 @@ It offers a wide variety of features, for example:
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
```text
|
|
36
|
-
Query: linter (
|
|
36
|
+
Query: linter (1 ms)
|
|
37
37
|
╰ Deprecated Functions (deprecated-functions):
|
|
38
38
|
╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0
|
|
39
39
|
╰ File Path Validity (file-path-validity):
|
|
40
40
|
╰ certain:
|
|
41
41
|
╰ Path `/root/x.txt` at 1.1-23
|
|
42
|
-
╰ Metadata: totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs:
|
|
42
|
+
╰ Metadata: totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 0
|
|
43
43
|
╰ Seeded Randomness (seeded-randomness):
|
|
44
44
|
╰ Metadata: consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 0, processTimeMs: 0
|
|
45
45
|
╰ Absolute Paths (absolute-file-paths):
|
|
46
46
|
╰ certain:
|
|
47
47
|
╰ Path `/root/x.txt` at 1.1-23
|
|
48
|
-
╰ Metadata: totalConsidered: 1, totalUnknown: 0, searchTimeMs:
|
|
48
|
+
╰ Metadata: totalConsidered: 1, totalUnknown: 0, searchTimeMs: 1, processTimeMs: 0
|
|
49
49
|
╰ Unused Definitions (unused-definitions):
|
|
50
|
-
╰ Metadata: totalConsidered: 0, searchTimeMs: 0, processTimeMs:
|
|
50
|
+
╰ Metadata: totalConsidered: 0, searchTimeMs: 0, processTimeMs: 0
|
|
51
51
|
╰ Naming Convention (naming-convention):
|
|
52
52
|
╰ Metadata: numMatches: 0, numBreak: 0, searchTimeMs: 0, processTimeMs: 0
|
|
53
53
|
╰ Network Functions (network-functions):
|
|
@@ -58,7 +58,7 @@ It offers a wide variety of features, for example:
|
|
|
58
58
|
╰ Metadata: consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0
|
|
59
59
|
╰ Useless Loops (useless-loop):
|
|
60
60
|
╰ Metadata: numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0
|
|
61
|
-
All queries together required ≈
|
|
61
|
+
All queries together required ≈1 ms (1ms accuracy, total 2 ms)
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
|
|
@@ -80,13 +80,13 @@ It offers a wide variety of features, for example:
|
|
|
80
80
|
|
|
81
81
|
_Results (prettified and summarized):_
|
|
82
82
|
|
|
83
|
-
Query: **linter** (
|
|
83
|
+
Query: **linter** (2 ms)\
|
|
84
84
|
╰ **Deprecated Functions** (deprecated-functions):\
|
|
85
|
-
╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs:
|
|
85
|
+
╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0</code>\
|
|
86
86
|
╰ **File Path Validity** (file-path-validity):\
|
|
87
87
|
╰ certain:\
|
|
88
88
|
╰ Path `/root/x.txt` at 1.1-23\
|
|
89
|
-
╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs:
|
|
89
|
+
╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
90
90
|
╰ **Seeded Randomness** (seeded-randomness):\
|
|
91
91
|
╰ _Metadata_: <code>consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
92
92
|
╰ **Absolute Paths** (absolute-file-paths):\
|
|
@@ -100,16 +100,16 @@ It offers a wide variety of features, for example:
|
|
|
100
100
|
╰ **Network Functions** (network-functions):\
|
|
101
101
|
╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
102
102
|
╰ **Dataframe Access Validation** (dataframe-access-validation):\
|
|
103
|
-
╰ _Metadata_: <code>numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs:
|
|
103
|
+
╰ _Metadata_: <code>numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
104
104
|
╰ **Dead Code** (dead-code):\
|
|
105
105
|
╰ _Metadata_: <code>consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
106
106
|
╰ **Useless Loops** (useless-loop):\
|
|
107
107
|
╰ _Metadata_: <code>numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
108
|
-
_All queries together required ≈
|
|
108
|
+
_All queries together required ≈2 ms (1ms accuracy, total 2 ms)_
|
|
109
109
|
|
|
110
110
|
<details> <summary style="color:gray">Show Detailed Results as Json</summary>
|
|
111
111
|
|
|
112
|
-
The analysis required
|
|
112
|
+
The analysis required _2.5 ms_ (including parsing and normalization and the query) within the generation environment.
|
|
113
113
|
|
|
114
114
|
In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR.
|
|
115
115
|
Please consult the [Interface](https://github.com/flowr-analysis/flowr/wiki/Interface) wiki page for more information on how to get those.
|
|
@@ -126,7 +126,7 @@ It offers a wide variety of features, for example:
|
|
|
126
126
|
".meta": {
|
|
127
127
|
"totalCalls": 0,
|
|
128
128
|
"totalFunctionDefinitions": 0,
|
|
129
|
-
"searchTimeMs":
|
|
129
|
+
"searchTimeMs": 1,
|
|
130
130
|
"processTimeMs": 0
|
|
131
131
|
}
|
|
132
132
|
},
|
|
@@ -149,7 +149,7 @@ It offers a wide variety of features, for example:
|
|
|
149
149
|
"totalUnknown": 0,
|
|
150
150
|
"totalWritesBeforeAlways": 0,
|
|
151
151
|
"totalValid": 0,
|
|
152
|
-
"searchTimeMs":
|
|
152
|
+
"searchTimeMs": 0,
|
|
153
153
|
"processTimeMs": 0
|
|
154
154
|
}
|
|
155
155
|
},
|
|
@@ -218,7 +218,7 @@ It offers a wide variety of features, for example:
|
|
|
218
218
|
"numAccesses": 0,
|
|
219
219
|
"totalAccessed": 0,
|
|
220
220
|
"searchTimeMs": 0,
|
|
221
|
-
"processTimeMs":
|
|
221
|
+
"processTimeMs": 0
|
|
222
222
|
}
|
|
223
223
|
},
|
|
224
224
|
"dead-code": {
|
|
@@ -239,11 +239,11 @@ It offers a wide variety of features, for example:
|
|
|
239
239
|
}
|
|
240
240
|
},
|
|
241
241
|
".meta": {
|
|
242
|
-
"timing":
|
|
242
|
+
"timing": 2
|
|
243
243
|
}
|
|
244
244
|
},
|
|
245
245
|
".meta": {
|
|
246
|
-
"timing":
|
|
246
|
+
"timing": 2
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
```
|
|
@@ -308,7 +308,7 @@ It offers a wide variety of features, for example:
|
|
|
308
308
|
|
|
309
309
|
```shell
|
|
310
310
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
311
|
-
flowR repl using flowR v2.9.
|
|
311
|
+
flowR repl using flowR v2.9.6, R grammar v14 (tree-sitter engine)
|
|
312
312
|
R> :query @static-slice (11@sum) file://test/testfiles/example.R
|
|
313
313
|
```
|
|
314
314
|
|
|
@@ -322,7 +322,7 @@ It offers a wide variety of features, for example:
|
|
|
322
322
|
N <- 10
|
|
323
323
|
for(i in 1:(N-1)) sum <- sum + i + w
|
|
324
324
|
sum
|
|
325
|
-
All queries together required ≈3 ms (1ms accuracy, total
|
|
325
|
+
All queries together required ≈3 ms (1ms accuracy, total 3 ms)
|
|
326
326
|
```
|
|
327
327
|
|
|
328
328
|
|
|
@@ -356,16 +356,16 @@ It offers a wide variety of features, for example:
|
|
|
356
356
|
|
|
357
357
|
|
|
358
358
|
* 🚀 **fast call-graph, data-, and control-flow graphs**\
|
|
359
|
-
Within just [<i><span title="This measurement is automatically fetched from the latest benchmark!">
|
|
360
|
-
_flowR_ can analyze the data- and control-flow of the average real-world R
|
|
361
|
-
and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/wiki/dataflow-graph) for more details on the dataflow graphs as well as call graphs.
|
|
359
|
+
Within just [<i><span title="This measurement is automatically fetched from the latest benchmark!">116.7 ms</span></i> (as of Feb 5, 2026)](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark),
|
|
360
|
+
_flowR_ can analyze the data- and control-flow of the average real-world R script. See the [benchmarks](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark) for more information,
|
|
361
|
+
and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/wiki/dataflow-graph) for more details on the [dataflow graphs](https://github.com/flowr-analysis/flowr/wiki/wiki/dataflow-graph) as well as [call graphs](https://github.com/flowr-analysis/flowr/wiki/wiki/dataflow-graph#perspectives-cg).
|
|
362
362
|
|
|
363
363
|
|
|
364
364
|
<details><summary>Example: Generating a dataflow graph with flowR</summary>
|
|
365
365
|
|
|
366
366
|
|
|
367
367
|
You can investigate flowR's analyses using the [REPL](https://github.com/flowr-analysis/flowr/wiki/Interface#using-the-repl).
|
|
368
|
-
Commands like <span title="Description (Repl Command, starred version): Returns the URL to mermaid.live; Base Command: Get mermaid code for the dataflow graph, start with 'file://' to indicate a file (aliases: :d*, :df*)">`:dataflow*`</span> allow you to view a dataflow graph for a given R script.
|
|
368
|
+
Commands like <span title="Description (Repl Command, starred version): Returns the URL to mermaid.live; Base Command: Get mermaid code for the dataflow graph, start with 'file://' to indicate a file (aliases: :d*, :df*)">`:dataflow*`</span> allow you to view a [dataflow graph](https://github.com/flowr-analysis/flowr/wiki/wiki/dataflow-graph) for a given R script.
|
|
369
369
|
|
|
370
370
|
Let's have a look at the following example:
|
|
371
371
|
|
|
@@ -386,13 +386,13 @@ It offers a wide variety of features, for example:
|
|
|
386
386
|
```
|
|
387
387
|
|
|
388
388
|
|
|
389
|
-
To get the dataflow graph for this script, you can use the following command:
|
|
389
|
+
To get the [dataflow graph](https://github.com/flowr-analysis/flowr/wiki/wiki/dataflow-graph) for this script, you can use the following command:
|
|
390
390
|
|
|
391
391
|
|
|
392
392
|
|
|
393
393
|
```shell
|
|
394
394
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
395
|
-
flowR repl using flowR v2.9.
|
|
395
|
+
flowR repl using flowR v2.9.6, R grammar v14 (tree-sitter engine)
|
|
396
396
|
R> :dataflow* test/testfiles/example.R
|
|
397
397
|
```
|
|
398
398
|
|
|
@@ -697,7 +697,7 @@ It offers a wide variety of features, for example:
|
|
|
697
697
|
```
|
|
698
698
|
|
|
699
699
|
|
|
700
|
-
(The analysis required
|
|
700
|
+
(The analysis required _1.9 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
|
|
701
701
|
|
|
702
702
|
|
|
703
703
|
|
|
@@ -724,8 +724,8 @@ If you are already using flowR and want to give feedback, please consider fillin
|
|
|
724
724
|
|
|
725
725
|
## ⭐ Getting Started
|
|
726
726
|
|
|
727
|
-
To get started with _flowR_ and its features, please check out the [Overview](https://github.com/flowr-analysis/flowr/wiki/
|
|
728
|
-
The [Setup](https://github.com/flowr-analysis/flowr/wiki/
|
|
727
|
+
To get started with _flowR_ and its features, please check out the [Overview](https://github.com/flowr-analysis/flowr/wiki/wiki/overview) wiki page.
|
|
728
|
+
The [Setup](https://github.com/flowr-analysis/flowr/wiki/wiki/setup) wiki page explains how you can download and setup _flowR_ on your system.
|
|
729
729
|
With docker 🐳️, the following line should be enough (and drop you directly into the read-eval-print loop):
|
|
730
730
|
|
|
731
731
|
|
|
@@ -753,7 +753,9 @@ If you want to use the same commands:
|
|
|
753
753
|
## 📜 More Information
|
|
754
754
|
|
|
755
755
|
For more details on how to use _flowR_ please refer to the [wiki pages](https://github.com/flowr-analysis/flowr/wiki),
|
|
756
|
-
as well as the deployed [code documentation](https://flowr-analysis.github.io/flowr/
|
|
756
|
+
as well as the deployed [code documentation](https://flowr-analysis.github.io/flowr/docs).
|
|
757
|
+
To cite flowR, please check out the publications below. To specifically refer to the source code,
|
|
758
|
+
please check out flowR's [Zenodo archive](https://zenodo.org/doi/10.5281/zenodo.13319290).
|
|
757
759
|
|
|
758
760
|
## 📃 Publications on flowR
|
|
759
761
|
|
|
@@ -860,7 +862,7 @@ please check out the following publications (if you find that a paper is missing
|
|
|
860
862
|
|
|
861
863
|
## 🚀 Contributing
|
|
862
864
|
|
|
863
|
-
We welcome every contribution! Please check out the [developer onboarding](https://github.com/flowr-analysis/flowr/wiki/
|
|
865
|
+
We welcome every contribution! Please check out the [developer onboarding](https://github.com/flowr-analysis/flowr/wiki/wiki/onboarding) section in the wiki for all the information you will need.
|
|
864
866
|
|
|
865
867
|
### Contributors
|
|
866
868
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DataFrameDomain = void 0;
|
|
4
|
-
const lattice_1 = require("../domains/lattice");
|
|
5
4
|
const positive_interval_domain_1 = require("../domains/positive-interval-domain");
|
|
6
5
|
const product_domain_1 = require("../domains/product-domain");
|
|
7
6
|
const set_range_domain_1 = require("../domains/set-range-domain");
|
|
@@ -53,7 +52,7 @@ class DataFrameDomain extends product_domain_1.ProductDomain {
|
|
|
53
52
|
}
|
|
54
53
|
static refine(value) {
|
|
55
54
|
if (value.colnames.isValue() && value.cols.isValue()) {
|
|
56
|
-
if (value.colnames.value.
|
|
55
|
+
if (value.colnames.value.min.size >= value.cols.value[1]) {
|
|
57
56
|
value.colnames = value.colnames.meet({ min: new Set(), range: value.colnames.value.min });
|
|
58
57
|
}
|
|
59
58
|
if (value.colnames.isValue()) {
|
package/benchmark/slicer.d.ts
CHANGED
package/benchmark/slicer.js
CHANGED
|
@@ -41,7 +41,6 @@ class BenchmarkSlicer {
|
|
|
41
41
|
perSliceMeasurements = new Map();
|
|
42
42
|
deltas = new Map();
|
|
43
43
|
parserName;
|
|
44
|
-
config;
|
|
45
44
|
context;
|
|
46
45
|
stats;
|
|
47
46
|
loadedXml;
|
|
@@ -63,7 +62,6 @@ class BenchmarkSlicer {
|
|
|
63
62
|
*/
|
|
64
63
|
async init(request, config, autoSelectIf, threshold) {
|
|
65
64
|
(0, assert_1.guard)(this.stats === undefined, 'cannot initialize the slicer twice');
|
|
66
|
-
this.config = config;
|
|
67
65
|
// we know these are in sync so we just cast to one of them
|
|
68
66
|
this.parser = await this.commonMeasurements.measure('initialize R session', async () => {
|
|
69
67
|
if (this.parserName === 'r-shell') {
|
|
@@ -227,11 +225,8 @@ class BenchmarkSlicer {
|
|
|
227
225
|
exports.benchmarkLogger.trace('try to extract the control flow graph');
|
|
228
226
|
this.guardActive();
|
|
229
227
|
(0, assert_1.guard)(this.normalizedAst !== undefined, 'normalizedAst should be defined for control flow extraction');
|
|
230
|
-
(0, assert_1.guard)(this.dataflow !== undefined, 'dataflow should be defined for control flow extraction');
|
|
231
|
-
(0, assert_1.guard)(this.config !== undefined, 'config should be defined for control flow extraction');
|
|
232
228
|
const ast = this.normalizedAst;
|
|
233
|
-
|
|
234
|
-
this.controlFlow = this.measureSimpleStep('extract control flow graph', () => (0, extract_cfg_1.extractCfg)(ast, this.context, dfg));
|
|
229
|
+
this.controlFlow = this.measureSimpleStep('extract control flow graph', () => (0, extract_cfg_1.extractCfg)(ast, this.context, undefined, undefined, true));
|
|
235
230
|
}
|
|
236
231
|
/**
|
|
237
232
|
* Infer the shape of data frames using abstract interpretation with {@link inferDataFrameShapes}
|
|
@@ -264,6 +259,7 @@ class BenchmarkSlicer {
|
|
|
264
259
|
this.measureSimpleStep('infer data frame shapes', () => inference.start());
|
|
265
260
|
const result = inference.getEndState();
|
|
266
261
|
stats.numberOfResultConstraints = result.value.size;
|
|
262
|
+
stats.sizeOfInfo = (0, size_of_1.safeSizeOf)([inference.getAbstractTrace()]);
|
|
267
263
|
for (const value of result.value.values()) {
|
|
268
264
|
if (value.isTop()) {
|
|
269
265
|
stats.numberOfResultingTop++;
|
|
@@ -283,10 +279,8 @@ class BenchmarkSlicer {
|
|
|
283
279
|
stats.numberOfEmptyNodes++;
|
|
284
280
|
return;
|
|
285
281
|
}
|
|
286
|
-
const state = inference.getAbstractState(node.info.id);
|
|
287
|
-
stats.sizeOfInfo += (0, size_of_1.safeSizeOf)([state]);
|
|
288
282
|
const nodeStats = {
|
|
289
|
-
numberOfEntries:
|
|
283
|
+
numberOfEntries: inference.getAbstractState(node.info.id)?.value.size ?? 0
|
|
290
284
|
};
|
|
291
285
|
if (operations !== undefined) {
|
|
292
286
|
nodeStats.mappedOperations = operations.map(op => op.operation);
|
package/benchmark/stats/print.js
CHANGED
|
@@ -268,6 +268,7 @@ Dataframe shape inference:
|
|
|
268
268
|
Number of abstract value nodes: ${formatSummarizedMeasure(stats.dataFrameShape.numberOfValueNodes)}
|
|
269
269
|
Number of entries per node: ${formatSummarizedMeasure(stats.dataFrameShape.numberOfEntriesPerNode)}
|
|
270
270
|
Number of operations: ${formatSummarizedMeasure(stats.dataFrameShape.numberOfOperations)}
|
|
271
|
+
Number of total exact: ${formatSummarizedMeasure(stats.dataFrameShape.numberOfTotalExact)}
|
|
271
272
|
Number of total values: ${formatSummarizedMeasure(stats.dataFrameShape.numberOfTotalValues)}
|
|
272
273
|
Number of total top: ${formatSummarizedMeasure(stats.dataFrameShape.numberOfTotalTop)}
|
|
273
274
|
Inferred column names per node: ${formatSummarizedMeasure(stats.dataFrameShape.inferredColNames)}
|
|
@@ -83,6 +83,8 @@ export interface UltimateSlicerStats {
|
|
|
83
83
|
export interface SummarizedDfShapeStats<T = number> extends Omit<SlicerStatsDfShape<T>, 'perNodeStats'> {
|
|
84
84
|
numberOfEntriesPerNode: SummarizedMeasurement;
|
|
85
85
|
numberOfOperations: T;
|
|
86
|
+
numberOfTotalConstraints: T;
|
|
87
|
+
numberOfTotalExact: T;
|
|
86
88
|
numberOfTotalValues: T;
|
|
87
89
|
numberOfTotalBottom: T;
|
|
88
90
|
numberOfTotalTop: T;
|
|
@@ -265,8 +265,10 @@ function summarizeDfShapeStats({ perNodeStats, ...stats }) {
|
|
|
265
265
|
...stats,
|
|
266
266
|
numberOfEntriesPerNode: (0, summarizer_1.summarizeMeasurement)(nodeStats.map(s => s.numberOfEntries)),
|
|
267
267
|
numberOfOperations: (0, arrays_1.arraySum)(nodeStats.map(s => s.mappedOperations?.length).filter(assert_1.isNotUndefined)),
|
|
268
|
+
numberOfTotalConstraints: nodeStats.filter(s => s.inferredColNames !== undefined && s.inferredColCount !== undefined && s.inferredRowCount !== undefined).length,
|
|
269
|
+
numberOfTotalExact: nodeStats.filter(s => s.approxRangeColNames === 0 && s.approxRangeColCount === 0 && s.approxRangeRowCount === 0).length,
|
|
268
270
|
numberOfTotalValues: nodeStats.filter(s => isValue(s.inferredColNames) && isValue(s.inferredColCount) && isValue(s.inferredRowCount)).length,
|
|
269
|
-
numberOfTotalBottom: nodeStats.filter(s => s.
|
|
271
|
+
numberOfTotalBottom: nodeStats.filter(s => isBottom(s.inferredColCount) && isBottom(s.inferredColCount) && isBottom(s.inferredRowCount)).length,
|
|
270
272
|
numberOfTotalTop: nodeStats.filter(s => isTop(s.inferredColNames) && isTop(s.inferredColCount) && isTop(s.inferredRowCount)).length,
|
|
271
273
|
inferredColNames: (0, summarizer_1.summarizeMeasurement)(nodeStats.map(s => s.inferredColNames).filter(isValue)),
|
|
272
274
|
approxRangeColNames: (0, summarizer_1.summarizeMeasurement)(nodeStats.map(s => s.approxRangeColNames).filter(assert_1.isNotUndefined).filter(isFinite)),
|
|
@@ -119,6 +119,8 @@ function summarizeAllSummarizedStats(stats) {
|
|
|
119
119
|
sizeOfInfo: (0, summarizer_1.summarizeMeasurement)(stats.map(s => s.dataFrameShape?.sizeOfInfo).filter(assert_1.isNotUndefined)),
|
|
120
120
|
numberOfEntriesPerNode: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataFrameShape?.numberOfEntriesPerNode).filter(assert_1.isNotUndefined)),
|
|
121
121
|
numberOfOperations: (0, summarizer_1.summarizeMeasurement)(stats.map(s => s.dataFrameShape?.numberOfOperations).filter(assert_1.isNotUndefined)),
|
|
122
|
+
numberOfTotalConstraints: (0, summarizer_1.summarizeMeasurement)(stats.map(s => s.dataFrameShape?.numberOfTotalConstraints).filter(assert_1.isNotUndefined)),
|
|
123
|
+
numberOfTotalExact: (0, summarizer_1.summarizeMeasurement)(stats.map(s => s.dataFrameShape?.numberOfTotalExact).filter(assert_1.isNotUndefined)),
|
|
122
124
|
numberOfTotalValues: (0, summarizer_1.summarizeMeasurement)(stats.map(s => s.dataFrameShape?.numberOfTotalValues).filter(assert_1.isNotUndefined)),
|
|
123
125
|
numberOfTotalBottom: (0, summarizer_1.summarizeMeasurement)(stats.map(s => s.dataFrameShape?.numberOfTotalBottom).filter(assert_1.isNotUndefined)),
|
|
124
126
|
numberOfTotalTop: (0, summarizer_1.summarizeMeasurement)(stats.map(s => s.dataFrameShape?.numberOfTotalTop).filter(assert_1.isNotUndefined)),
|
|
@@ -203,6 +205,8 @@ function summarizeAllUltimateStats(stats) {
|
|
|
203
205
|
sizeOfInfo: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataFrameShape?.sizeOfInfo).filter(assert_1.isNotUndefined)),
|
|
204
206
|
numberOfEntriesPerNode: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataFrameShape?.numberOfEntriesPerNode).filter(assert_1.isNotUndefined)),
|
|
205
207
|
numberOfOperations: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataFrameShape?.numberOfOperations).filter(assert_1.isNotUndefined)),
|
|
208
|
+
numberOfTotalConstraints: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataFrameShape?.numberOfTotalConstraints).filter(assert_1.isNotUndefined)),
|
|
209
|
+
numberOfTotalExact: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataFrameShape?.numberOfTotalExact).filter(assert_1.isNotUndefined)),
|
|
206
210
|
numberOfTotalValues: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataFrameShape?.numberOfTotalValues).filter(assert_1.isNotUndefined)),
|
|
207
211
|
numberOfTotalBottom: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataFrameShape?.numberOfTotalBottom).filter(assert_1.isNotUndefined)),
|
|
208
212
|
numberOfTotalTop: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataFrameShape?.numberOfTotalTop).filter(assert_1.isNotUndefined)),
|
|
@@ -12,6 +12,7 @@ declare const _commands: {
|
|
|
12
12
|
readonly parse: ReplCodeCommand;
|
|
13
13
|
readonly normalize: ReplCodeCommand;
|
|
14
14
|
readonly 'normalize*': ReplCodeCommand;
|
|
15
|
+
readonly 'normalize#': ReplCodeCommand;
|
|
15
16
|
readonly dataflow: ReplCodeCommand;
|
|
16
17
|
readonly 'dataflow*': ReplCodeCommand;
|
|
17
18
|
readonly dataflowsimple: ReplCodeCommand;
|
|
@@ -79,6 +79,7 @@ const _commands = {
|
|
|
79
79
|
'parse': repl_parse_1.parseCommand,
|
|
80
80
|
'normalize': repl_normalize_1.normalizeCommand,
|
|
81
81
|
'normalize*': repl_normalize_1.normalizeStarCommand,
|
|
82
|
+
'normalize#': repl_normalize_1.normalizeHashCommand,
|
|
82
83
|
'dataflow': repl_dataflow_1.dataflowCommand,
|
|
83
84
|
'dataflow*': repl_dataflow_1.dataflowStarCommand,
|
|
84
85
|
'dataflowsimple': repl_dataflow_1.dataflowSimplifiedCommand,
|
|
@@ -33,11 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.normalizeStarCommand = exports.normalizeCommand = void 0;
|
|
36
|
+
exports.normalizeHashCommand = exports.normalizeStarCommand = exports.normalizeCommand = void 0;
|
|
37
37
|
const retriever_1 = require("../../../r-bridge/retriever");
|
|
38
38
|
const ast_1 = require("../../../util/mermaid/ast");
|
|
39
39
|
const ansi_1 = require("../../../util/text/ansi");
|
|
40
40
|
const core_1 = require("../core");
|
|
41
|
+
const defaultmap_1 = require("../../../util/collections/defaultmap");
|
|
42
|
+
const visitor_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/visitor");
|
|
41
43
|
function formatInfo(out, type, meta) {
|
|
42
44
|
return out.formatter.format(`Copied ${type} to clipboard (normalize: ${meta['.meta'].timing + 'ms'}).`, { color: 7 /* Colors.White */, effect: ansi_1.ColorEffect.Foreground, style: 3 /* FontStyles.Italic */ });
|
|
43
45
|
}
|
|
@@ -79,4 +81,29 @@ exports.normalizeStarCommand = {
|
|
|
79
81
|
catch { /* do nothing this is a service thing */ }
|
|
80
82
|
}
|
|
81
83
|
};
|
|
84
|
+
exports.normalizeHashCommand = {
|
|
85
|
+
description: 'Returns summarization stats for the normalized AST',
|
|
86
|
+
isCodeCommand: true,
|
|
87
|
+
usageExample: ':normalize#',
|
|
88
|
+
aliases: ['n#'],
|
|
89
|
+
script: false,
|
|
90
|
+
argsParser: (args) => (0, core_1.handleString)(args),
|
|
91
|
+
fn: async ({ output, analyzer }) => {
|
|
92
|
+
const result = await analyzer.normalize();
|
|
93
|
+
const counts = new defaultmap_1.DefaultMap(() => 0);
|
|
94
|
+
let total = 0;
|
|
95
|
+
const files = result.ast.files.length;
|
|
96
|
+
(0, visitor_1.visitAst)(result.ast.files.map(f => f.root), n => {
|
|
97
|
+
counts.set(n.type, counts.get(n.type) + 1);
|
|
98
|
+
total++;
|
|
99
|
+
});
|
|
100
|
+
const num = (s, pad = 0) => output.formatter.format(s.toString().padStart(pad, ' '), { color: 6 /* Colors.Cyan */, effect: ansi_1.ColorEffect.Foreground });
|
|
101
|
+
output.stdout(output.formatter.format('Calculated in ' + result['.meta'].timing + 'ms', { color: 7 /* Colors.White */, effect: ansi_1.ColorEffect.Foreground, style: 3 /* FontStyles.Italic */ }));
|
|
102
|
+
output.stdout(`${num(total)} nodes total over ${num(files)} file(s)`);
|
|
103
|
+
const longestType = counts.keys().map(p => p.toString().length).reduce((a, b) => Math.max(a, b), 0);
|
|
104
|
+
for (const [type, count] of counts.entries().toArray().sort((a, b) => b[1] - a[1])) {
|
|
105
|
+
output.stdout(` ${(type + ':').padEnd(longestType + 1, ' ')} ${num(count, 7)}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
82
109
|
//# sourceMappingURL=repl-normalize.js.map
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BasicCfgGuidedVisitor = void 0;
|
|
4
4
|
const control_flow_graph_1 = require("./control-flow-graph");
|
|
5
5
|
const assert_1 = require("../util/assert");
|
|
6
|
-
const invert_cfg_1 = require("./invert-cfg");
|
|
7
6
|
/**
|
|
8
7
|
* In contrast to {@link visitCfgInOrder} and {@link visitCfgInReverseOrder}, this visitor is not a simple visitor
|
|
9
8
|
* and serves as the basis for a variety of more complicated visiting orders of the control flow graph.
|
|
@@ -34,8 +33,7 @@ class BasicCfgGuidedVisitor {
|
|
|
34
33
|
const graph = this.config.controlFlow.graph;
|
|
35
34
|
let getNext;
|
|
36
35
|
if (this.config.defaultVisitingOrder === 'forward') {
|
|
37
|
-
|
|
38
|
-
getNext = (node) => inverseGraph.outgoingEdges(node)?.keys().toArray().reverse();
|
|
36
|
+
getNext = (node) => graph.ingoingEdges(node)?.keys().toArray().reverse();
|
|
39
37
|
}
|
|
40
38
|
else {
|
|
41
39
|
getNext = (node) => graph.outgoingEdges(node)?.keys();
|
|
@@ -9,7 +9,6 @@ const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-functi
|
|
|
9
9
|
const general_1 = require("../dataflow/eval/values/general");
|
|
10
10
|
const r_value_1 = require("../dataflow/eval/values/r-value");
|
|
11
11
|
const simple_visitor_1 = require("./simple-visitor");
|
|
12
|
-
const invert_cfg_1 = require("./invert-cfg");
|
|
13
12
|
class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor {
|
|
14
13
|
cachedConditions = new Map();
|
|
15
14
|
cachedStatements = new Map();
|
|
@@ -41,24 +40,24 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
|
|
|
41
40
|
this.cachedConditions.set(id, value ? logic_1.Ternary.Always : logic_1.Ternary.Never);
|
|
42
41
|
}
|
|
43
42
|
startVisitor() {
|
|
44
|
-
|
|
45
|
-
for (const [from, targets] of
|
|
43
|
+
const cfg = this.config.controlFlow.graph;
|
|
44
|
+
for (const [from, targets] of cfg.edges()) {
|
|
46
45
|
for (const [target, edge] of targets) {
|
|
47
46
|
if (edge.label === 1 /* CfgEdgeType.Cd */) {
|
|
48
47
|
const og = this.getValue(edge.caused);
|
|
49
48
|
if (og === logic_1.Ternary.Always && edge.when === 'FALSE') {
|
|
50
|
-
|
|
49
|
+
cfg.removeEdge(from, target);
|
|
51
50
|
}
|
|
52
51
|
else if (og === logic_1.Ternary.Never && edge.when === 'TRUE') {
|
|
53
|
-
|
|
52
|
+
cfg.removeEdge(from, target);
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
55
|
else if (edge.label === 0 /* CfgEdgeType.Fd */ && this.isUnconditionalJump(target)) {
|
|
57
56
|
// for each unconditional jump, we find the corresponding end/exit nodes and remove any flow edges
|
|
58
57
|
for (const end of this.getCfgVertex(target)?.end ?? []) {
|
|
59
|
-
for (const [target, edge] of
|
|
58
|
+
for (const [target, edge] of cfg.ingoingEdges(end) ?? []) {
|
|
60
59
|
if (edge.label === 0 /* CfgEdgeType.Fd */) {
|
|
61
|
-
|
|
60
|
+
cfg.removeEdge(target, end);
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
}
|
|
@@ -67,6 +66,9 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
|
|
|
67
66
|
}
|
|
68
67
|
}
|
|
69
68
|
handleValuesFor(id, valueId) {
|
|
69
|
+
if (this.cachedConditions.has(id)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
70
72
|
const values = (0, general_1.valueSetGuard)((0, alias_tracking_1.resolveIdToValue)(valueId, {
|
|
71
73
|
graph: this.config.dfg,
|
|
72
74
|
full: true,
|
|
@@ -74,7 +76,7 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
|
|
|
74
76
|
resolve: this.config.ctx.config.solver.variables,
|
|
75
77
|
ctx: this.config.ctx,
|
|
76
78
|
}));
|
|
77
|
-
if (values === undefined || values.elements.length !== 1 || values.elements[0].type
|
|
79
|
+
if (values === undefined || values.elements.length !== 1 || values.elements[0].type !== 'logical' || !(0, r_value_1.isValue)(values.elements[0].value)) {
|
|
78
80
|
this.unableToCalculateValue(id);
|
|
79
81
|
return;
|
|
80
82
|
}
|
|
@@ -99,7 +101,7 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
|
|
|
99
101
|
idMap: this.config.normalizedAst.idMap,
|
|
100
102
|
ctx: this.config.ctx,
|
|
101
103
|
}));
|
|
102
|
-
if (values === undefined || values.elements.length !== 1 || values.elements[0].type
|
|
104
|
+
if (values === undefined || values.elements.length !== 1 || values.elements[0].type !== 'logical' || !(0, r_value_1.isValue)(values.elements[0].value)) {
|
|
103
105
|
return undefined;
|
|
104
106
|
}
|
|
105
107
|
return Boolean(values.elements[0].value);
|
|
@@ -114,6 +116,9 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
|
|
|
114
116
|
this.cachedStatements.set(data.call.id, true);
|
|
115
117
|
}
|
|
116
118
|
onStopIfNotCall(data) {
|
|
119
|
+
if (this.cachedStatements.has(data.call.id)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
117
122
|
const arg = this.getBoolArgValue(data);
|
|
118
123
|
if (arg !== undefined) {
|
|
119
124
|
this.cachedStatements.set(data.call.id, !arg);
|
|
@@ -133,7 +138,7 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
|
|
|
133
138
|
}
|
|
134
139
|
this.inTry.add(n);
|
|
135
140
|
return false;
|
|
136
|
-
}
|
|
141
|
+
});
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
144
|
/** Breaks unsatisfiable control dependencies */
|
|
@@ -174,6 +174,8 @@ export declare class ControlFlowGraph<Vertex extends CfgSimpleVertex = CfgSimple
|
|
|
174
174
|
private readonly bbChildren;
|
|
175
175
|
/** basic block agnostic edges */
|
|
176
176
|
private readonly edgeInformation;
|
|
177
|
+
/** reverse edges for bidirectional mapping */
|
|
178
|
+
private readonly reverseEdgeInfo;
|
|
177
179
|
/** used as an optimization to avoid unnecessary lookups */
|
|
178
180
|
private _mayHaveBasicBlocks;
|
|
179
181
|
/**
|
|
@@ -192,7 +194,7 @@ export declare class ControlFlowGraph<Vertex extends CfgSimpleVertex = CfgSimple
|
|
|
192
194
|
*/
|
|
193
195
|
addEdges(from: NodeId, to: Map<NodeId, CfgEdge>): this;
|
|
194
196
|
outgoingEdges(node: NodeId): ReadonlyMap<NodeId, CfgEdge> | undefined;
|
|
195
|
-
ingoingEdges(
|
|
197
|
+
ingoingEdges(node: NodeId): ReadonlyMap<NodeId, CfgEdge> | undefined;
|
|
196
198
|
rootIds(): ReadonlySet<NodeId>;
|
|
197
199
|
vertices(includeBasicBlockElements?: boolean): ReadonlyMap<NodeId, CfgSimpleVertex>;
|
|
198
200
|
getBasicBlock(elemId: NodeId): CfgBasicBlockVertex | undefined;
|
|
@@ -77,6 +77,8 @@ class ControlFlowGraph {
|
|
|
77
77
|
bbChildren = new Map();
|
|
78
78
|
/** basic block agnostic edges */
|
|
79
79
|
edgeInformation = new Map();
|
|
80
|
+
/** reverse edges for bidirectional mapping */
|
|
81
|
+
reverseEdgeInfo = new Map();
|
|
80
82
|
/** used as an optimization to avoid unnecessary lookups */
|
|
81
83
|
_mayHaveBasicBlocks = false;
|
|
82
84
|
/**
|
|
@@ -111,33 +113,27 @@ class ControlFlowGraph {
|
|
|
111
113
|
this.edgeInformation.set(from, edgesFrom);
|
|
112
114
|
}
|
|
113
115
|
edgesFrom.set(to, edge);
|
|
116
|
+
const edgesTo = this.reverseEdgeInfo.get(to) ?? new Map();
|
|
117
|
+
if (!this.reverseEdgeInfo.has(to)) {
|
|
118
|
+
this.reverseEdgeInfo.set(to, edgesTo);
|
|
119
|
+
}
|
|
120
|
+
edgesTo.set(from, edge);
|
|
114
121
|
return this;
|
|
115
122
|
}
|
|
116
123
|
/**
|
|
117
124
|
* Add multiple edges from a given source vertex to the control flow graph.
|
|
118
125
|
*/
|
|
119
126
|
addEdges(from, to) {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
this.edgeInformation.set(from, edgesFrom);
|
|
123
|
-
}
|
|
124
|
-
for (const [toId, edge] of to) {
|
|
125
|
-
edgesFrom.set(toId, edge);
|
|
127
|
+
for (const [toNode, edge] of to) {
|
|
128
|
+
this.addEdge(from, toNode, edge);
|
|
126
129
|
}
|
|
127
130
|
return this;
|
|
128
131
|
}
|
|
129
132
|
outgoingEdges(node) {
|
|
130
133
|
return this.edgeInformation.get(node);
|
|
131
134
|
}
|
|
132
|
-
ingoingEdges(
|
|
133
|
-
|
|
134
|
-
for (const [source, outgoing] of this.edgeInformation.entries()) {
|
|
135
|
-
const o = outgoing.get(id);
|
|
136
|
-
if (o) {
|
|
137
|
-
edges.set(source, o);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return edges;
|
|
135
|
+
ingoingEdges(node) {
|
|
136
|
+
return this.reverseEdgeInfo.get(node);
|
|
141
137
|
}
|
|
142
138
|
rootIds() {
|
|
143
139
|
return this.rootVertices;
|
|
@@ -208,6 +204,7 @@ class ControlFlowGraph {
|
|
|
208
204
|
removeVertex(id) {
|
|
209
205
|
this.vertexInformation.delete(id);
|
|
210
206
|
this.edgeInformation.delete(id);
|
|
207
|
+
this.reverseEdgeInfo.delete(id);
|
|
211
208
|
this.bbChildren.delete(id);
|
|
212
209
|
// remove all bbChildren with id as target
|
|
213
210
|
for (const [a, b] of this.bbChildren.entries()) {
|
|
@@ -218,6 +215,9 @@ class ControlFlowGraph {
|
|
|
218
215
|
for (const edges of this.edgeInformation.values()) {
|
|
219
216
|
edges.delete(id);
|
|
220
217
|
}
|
|
218
|
+
for (const edges of this.reverseEdgeInfo.values()) {
|
|
219
|
+
edges.delete(id);
|
|
220
|
+
}
|
|
221
221
|
this.rootVertices.delete(id);
|
|
222
222
|
return this;
|
|
223
223
|
}
|
|
@@ -227,13 +227,20 @@ class ControlFlowGraph {
|
|
|
227
227
|
* @see {@link ControlFlowGraph#removeVertex|removeVertex()} - to remove a vertex and all its edges
|
|
228
228
|
*/
|
|
229
229
|
removeEdge(from, to) {
|
|
230
|
-
const
|
|
231
|
-
if (
|
|
232
|
-
|
|
233
|
-
if (
|
|
230
|
+
const edgesFrom = this.edgeInformation.get(from);
|
|
231
|
+
if (edgesFrom) {
|
|
232
|
+
edgesFrom.delete(to);
|
|
233
|
+
if (edgesFrom.size === 0) {
|
|
234
234
|
this.edgeInformation.delete(from);
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
+
const edgesTo = this.reverseEdgeInfo.get(to);
|
|
238
|
+
if (edgesTo) {
|
|
239
|
+
edgesTo.delete(from);
|
|
240
|
+
if (edgesTo.size === 0) {
|
|
241
|
+
this.reverseEdgeInfo.delete(to);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
237
244
|
return this;
|
|
238
245
|
}
|
|
239
246
|
/** merges b into a */
|