@eagleoutice/flowr 2.9.7 → 2.9.8
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 +21 -21
- package/abstract-interpretation/absint-visitor.d.ts +5 -4
- package/abstract-interpretation/absint-visitor.js +12 -11
- package/control-flow/basic-cfg-guided-visitor.d.ts +3 -3
- package/control-flow/basic-cfg-guided-visitor.js +7 -6
- package/control-flow/cfg-dead-code.js +12 -9
- package/control-flow/cfg-properties.d.ts +2 -2
- package/control-flow/cfg-properties.js +3 -2
- package/control-flow/cfg-to-basic-blocks.js +9 -12
- package/control-flow/control-flow-graph.d.ts +382 -78
- package/control-flow/control-flow-graph.js +591 -106
- package/control-flow/dfg-cfg-guided-visitor.d.ts +4 -4
- package/control-flow/dfg-cfg-guided-visitor.js +3 -3
- package/control-flow/diff-cfg.js +35 -29
- package/control-flow/extract-cfg.d.ts +3 -3
- package/control-flow/extract-cfg.js +145 -125
- package/control-flow/happens-before.js +2 -1
- package/control-flow/semantic-cfg-guided-visitor.js +2 -1
- package/control-flow/simple-visitor.js +8 -6
- package/control-flow/syntax-cfg-guided-visitor.js +2 -1
- package/control-flow/useless-loop.js +2 -1
- package/documentation/wiki-cfg.js +21 -9
- package/documentation/wiki-mk/doc-maker.js +1 -1
- package/package.json +1 -1
- package/queries/catalog/control-flow-query/control-flow-query-format.js +1 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.d.ts +2 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +19 -19
- package/search/search-executor/search-generators.js +2 -2
- package/util/assert.d.ts +3 -3
- package/util/mermaid/cfg.d.ts +1 -7
- package/util/mermaid/cfg.js +37 -44
- package/util/version.js +1 -1
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.7, R grammar v14 (tree-sitter engine)
|
|
28
28
|
R> :query @linter "read.csv(\"/root/x.txt\")"
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -33,19 +33,19 @@ It offers a wide variety of features, for example:
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
```text
|
|
36
|
-
Query: linter (
|
|
36
|
+
Query: linter (2 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: 1, 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: 0, processTimeMs: 0
|
|
49
49
|
╰ Unused Definitions (unused-definitions):
|
|
50
50
|
╰ Metadata: totalConsidered: 0, searchTimeMs: 0, processTimeMs: 0
|
|
51
51
|
╰ Naming Convention (naming-convention):
|
|
@@ -53,12 +53,12 @@ It offers a wide variety of features, for example:
|
|
|
53
53
|
╰ Network Functions (network-functions):
|
|
54
54
|
╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0
|
|
55
55
|
╰ Dataframe Access Validation (dataframe-access-validation):
|
|
56
|
-
╰ Metadata: numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs:
|
|
56
|
+
╰ Metadata: numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 1, processTimeMs: 0
|
|
57
57
|
╰ Dead Code (dead-code):
|
|
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 ≈2 ms (1ms accuracy, total 3 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** (3 ms)\
|
|
84
84
|
╰ **Deprecated Functions** (deprecated-functions):\
|
|
85
|
-
╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs:
|
|
85
|
+
╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, 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: 1, 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: 1</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 ≈3 ms (1ms accuracy, total 3 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 _3.3 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": 0,
|
|
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": 1,
|
|
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": 1
|
|
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": 3
|
|
243
243
|
}
|
|
244
244
|
},
|
|
245
245
|
".meta": {
|
|
246
|
-
"timing":
|
|
246
|
+
"timing": 3
|
|
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.7, R grammar v14 (tree-sitter engine)
|
|
312
312
|
R> :query @static-slice (11@sum) file://test/testfiles/example.R
|
|
313
313
|
```
|
|
314
314
|
|
|
@@ -356,7 +356,7 @@ 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!">
|
|
359
|
+
Within just [<i><span title="This measurement is automatically fetched from the latest benchmark!">111.7 ms</span></i> (as of Feb 7, 2026)](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark),
|
|
360
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
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
|
|
|
@@ -392,7 +392,7 @@ It offers a wide variety of features, for example:
|
|
|
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.7, 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 _1.
|
|
700
|
+
(The analysis required _1.8 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
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ControlFlowInformation } from '../control-flow/control-flow-graph';
|
|
2
|
+
import { CfgVertex } from '../control-flow/control-flow-graph';
|
|
2
3
|
import type { SemanticCfgGuidedVisitorConfiguration } from '../control-flow/semantic-cfg-guided-visitor';
|
|
3
4
|
import { SemanticCfgGuidedVisitor } from '../control-flow/semantic-cfg-guided-visitor';
|
|
4
5
|
import { BuiltInProcName } from '../dataflow/environments/built-in';
|
|
@@ -98,15 +99,15 @@ export declare abstract class AbstractInterpretationVisitor<Domain extends AnyAb
|
|
|
98
99
|
* Checks whether to continue visiting the control flow graph after a widening point.
|
|
99
100
|
* By default, we only continue visiting if the widening point is visited for the first time or the abstract state at the widening point changed.
|
|
100
101
|
*/
|
|
101
|
-
protected shouldContinueVisiting(wideningPoint:
|
|
102
|
+
protected shouldContinueVisiting(wideningPoint: CfgVertex, oldState: StateAbstractDomain<Domain>): boolean;
|
|
102
103
|
/**
|
|
103
104
|
* Checks whether a control flow graph vertex should be skipped during visitation.
|
|
104
105
|
* By default, we only process vertices of leaf nodes and exit vertices (no entry nodes of complex nodes).
|
|
105
106
|
*/
|
|
106
|
-
protected shouldSkipVertex(vertex:
|
|
107
|
+
protected shouldSkipVertex(vertex: CfgVertex): boolean;
|
|
107
108
|
/**
|
|
108
109
|
* Whether widening should be performed at a widening point.
|
|
109
110
|
* By default, we perform widening when the number of visitation of the widening point reaches the widening threshold of the config.
|
|
110
111
|
*/
|
|
111
|
-
protected shouldWiden(wideningPoint:
|
|
112
|
+
protected shouldWiden(wideningPoint: CfgVertex): boolean;
|
|
112
113
|
}
|
|
@@ -100,7 +100,7 @@ class AbstractInterpretationVisitor extends semantic_cfg_guided_visitor_1.Semant
|
|
|
100
100
|
*/
|
|
101
101
|
getEndState() {
|
|
102
102
|
const exitPoints = this.config.controlFlow.exitPoints.map(id => this.getCfgVertex(id)).filter(assert_1.isNotUndefined);
|
|
103
|
-
const exitNodes = exitPoints.map(vertex =>
|
|
103
|
+
const exitNodes = exitPoints.map(vertex => control_flow_graph_1.CfgVertex.getRootId(vertex)).filter(assert_1.isNotUndefined);
|
|
104
104
|
const states = exitNodes.map(node => this.getAbstractState(node)).filter(assert_1.isNotUndefined);
|
|
105
105
|
return this.config.domain.bottom().joinAll(states);
|
|
106
106
|
}
|
|
@@ -121,14 +121,14 @@ class AbstractInterpretationVisitor extends semantic_cfg_guided_visitor_1.Semant
|
|
|
121
121
|
if (vertex === undefined) {
|
|
122
122
|
return true;
|
|
123
123
|
}
|
|
124
|
-
const nodeId =
|
|
124
|
+
const nodeId = control_flow_graph_1.CfgVertex.getRootId(vertex);
|
|
125
125
|
if (this.isWideningPoint(nodeId)) {
|
|
126
126
|
// only check widening points at the entry vertex
|
|
127
|
-
if (
|
|
127
|
+
if (control_flow_graph_1.CfgVertex.isMarker(vertex)) {
|
|
128
128
|
return true;
|
|
129
129
|
}
|
|
130
130
|
const oldState = this.getAbstractState(nodeId) ?? this.config.domain.bottom();
|
|
131
|
-
const predecessorDomains = this.getPredecessorNodes(vertex
|
|
131
|
+
const predecessorDomains = this.getPredecessorNodes(control_flow_graph_1.CfgVertex.getId(vertex)).map(pred => this.getAbstractState(pred)).filter(assert_1.isNotUndefined);
|
|
132
132
|
this.currentState = this.config.domain.bottom().joinAll(predecessorDomains);
|
|
133
133
|
if (this.shouldWiden(vertex)) {
|
|
134
134
|
this.currentState = oldState.widen(this.currentState);
|
|
@@ -139,7 +139,7 @@ class AbstractInterpretationVisitor extends semantic_cfg_guided_visitor_1.Semant
|
|
|
139
139
|
else if (this.shouldSkipVertex(vertex)) {
|
|
140
140
|
return true;
|
|
141
141
|
}
|
|
142
|
-
const predecessorDomains = this.getPredecessorNodes(vertex
|
|
142
|
+
const predecessorDomains = this.getPredecessorNodes(control_flow_graph_1.CfgVertex.getId(vertex)).map(pred => this.getAbstractState(pred)).filter(assert_1.isNotUndefined);
|
|
143
143
|
this.currentState = this.config.domain.bottom().joinAll(predecessorDomains);
|
|
144
144
|
this.onVisitNode(vertexId);
|
|
145
145
|
// discard the inferred abstract state when encountering functions with unknown side effects (e.g. `eval`)
|
|
@@ -211,10 +211,10 @@ class AbstractInterpretationVisitor extends semantic_cfg_guided_visitor_1.Semant
|
|
|
211
211
|
return [];
|
|
212
212
|
}
|
|
213
213
|
else if (this.shouldSkipVertex(vertex)) {
|
|
214
|
-
return this.getPredecessorNodes(vertex
|
|
214
|
+
return this.getPredecessorNodes(control_flow_graph_1.CfgVertex.getId(vertex));
|
|
215
215
|
}
|
|
216
216
|
else {
|
|
217
|
-
return [
|
|
217
|
+
return [control_flow_graph_1.CfgVertex.getRootId(vertex)];
|
|
218
218
|
}
|
|
219
219
|
})
|
|
220
220
|
.toArray() ?? [];
|
|
@@ -248,8 +248,9 @@ class AbstractInterpretationVisitor extends semantic_cfg_guided_visitor_1.Semant
|
|
|
248
248
|
* By default, we only continue visiting if the widening point is visited for the first time or the abstract state at the widening point changed.
|
|
249
249
|
*/
|
|
250
250
|
shouldContinueVisiting(wideningPoint, oldState) {
|
|
251
|
-
const
|
|
252
|
-
this.visited.
|
|
251
|
+
const wid = control_flow_graph_1.CfgVertex.getId(wideningPoint);
|
|
252
|
+
const visitedCount = this.visited.get(wid) ?? 0;
|
|
253
|
+
this.visited.set(wid, visitedCount + 1);
|
|
253
254
|
return visitedCount === 0 || !oldState.equals(this.currentState);
|
|
254
255
|
}
|
|
255
256
|
/**
|
|
@@ -257,14 +258,14 @@ class AbstractInterpretationVisitor extends semantic_cfg_guided_visitor_1.Semant
|
|
|
257
258
|
* By default, we only process vertices of leaf nodes and exit vertices (no entry nodes of complex nodes).
|
|
258
259
|
*/
|
|
259
260
|
shouldSkipVertex(vertex) {
|
|
260
|
-
return vertex
|
|
261
|
+
return !control_flow_graph_1.CfgVertex.isMarker(vertex) && !control_flow_graph_1.CfgVertex.isBlock(vertex) && control_flow_graph_1.CfgVertex.getEnd(vertex) !== undefined;
|
|
261
262
|
}
|
|
262
263
|
/**
|
|
263
264
|
* Whether widening should be performed at a widening point.
|
|
264
265
|
* By default, we perform widening when the number of visitation of the widening point reaches the widening threshold of the config.
|
|
265
266
|
*/
|
|
266
267
|
shouldWiden(wideningPoint) {
|
|
267
|
-
return (this.visited.get(wideningPoint
|
|
268
|
+
return (this.visited.get(control_flow_graph_1.CfgVertex.getId(wideningPoint)) ?? 0) >= this.config.ctx.config.abstractInterpretation.wideningThreshold;
|
|
268
269
|
}
|
|
269
270
|
}
|
|
270
271
|
exports.AbstractInterpretationVisitor = AbstractInterpretationVisitor;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CfgBasicBlockVertex, type
|
|
1
|
+
import { type CfgBasicBlockVertex, type CfgMarkerVertex, type CfgExpressionVertex, CfgVertex, type CfgStatementVertex, type ControlFlowInformation } from './control-flow-graph';
|
|
2
2
|
import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
3
3
|
export interface BasicCfgGuidedVisitorConfiguration<ControlFlow extends ControlFlowInformation = ControlFlowInformation> {
|
|
4
4
|
readonly controlFlow: ControlFlow;
|
|
@@ -28,10 +28,10 @@ export declare class BasicCfgGuidedVisitor<ControlFlow extends ControlFlowInform
|
|
|
28
28
|
/**
|
|
29
29
|
* Get the control flow vertex for the given node id or fail if it does not exist.
|
|
30
30
|
*/
|
|
31
|
-
protected getCfgVertex(id: NodeId):
|
|
31
|
+
protected getCfgVertex(id: NodeId): CfgVertex | undefined;
|
|
32
32
|
protected onVisitNode(node: NodeId): void;
|
|
33
33
|
protected onBasicBlockNode(node: CfgBasicBlockVertex): void;
|
|
34
34
|
protected onStatementNode(_node: CfgStatementVertex): void;
|
|
35
35
|
protected onExpressionNode(_node: CfgExpressionVertex): void;
|
|
36
|
-
protected onEndMarkerNode(_node:
|
|
36
|
+
protected onEndMarkerNode(_node: CfgMarkerVertex): void;
|
|
37
37
|
}
|
|
@@ -66,7 +66,7 @@ class BasicCfgGuidedVisitor {
|
|
|
66
66
|
if (vertex === undefined) {
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
|
-
const type = vertex
|
|
69
|
+
const type = control_flow_graph_1.CfgVertex.getType(vertex);
|
|
70
70
|
switch (type) {
|
|
71
71
|
case control_flow_graph_1.CfgVertexType.Statement:
|
|
72
72
|
this.onStatementNode(vertex);
|
|
@@ -74,7 +74,7 @@ class BasicCfgGuidedVisitor {
|
|
|
74
74
|
case control_flow_graph_1.CfgVertexType.Expression:
|
|
75
75
|
this.onExpressionNode(vertex);
|
|
76
76
|
break;
|
|
77
|
-
case control_flow_graph_1.CfgVertexType.
|
|
77
|
+
case control_flow_graph_1.CfgVertexType.Marker:
|
|
78
78
|
this.onEndMarkerNode(vertex);
|
|
79
79
|
break;
|
|
80
80
|
case control_flow_graph_1.CfgVertexType.Block:
|
|
@@ -85,14 +85,15 @@ class BasicCfgGuidedVisitor {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
onBasicBlockNode(node) {
|
|
88
|
+
const elems = control_flow_graph_1.CfgVertex.getBasicBlockElements(node);
|
|
88
89
|
if (this.config.defaultVisitingOrder === 'forward') {
|
|
89
|
-
for (const elem of
|
|
90
|
-
this.visitNode(elem
|
|
90
|
+
for (const elem of elems.toReversed()) {
|
|
91
|
+
this.visitNode(control_flow_graph_1.CfgVertex.getId(elem));
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
else {
|
|
94
|
-
for (const elem of
|
|
95
|
-
this.visitNode(elem
|
|
95
|
+
for (const elem of elems) {
|
|
96
|
+
this.visitNode(control_flow_graph_1.CfgVertex.getId(elem));
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.cfgAnalyzeDeadCode = cfgAnalyzeDeadCode;
|
|
4
|
+
const control_flow_graph_1 = require("./control-flow-graph");
|
|
4
5
|
const logic_1 = require("../util/logic");
|
|
5
6
|
const semantic_cfg_guided_visitor_1 = require("./semantic-cfg-guided-visitor");
|
|
6
7
|
const alias_tracking_1 = require("../dataflow/eval/resolve/alias-tracking");
|
|
@@ -9,6 +10,7 @@ const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-functi
|
|
|
9
10
|
const general_1 = require("../dataflow/eval/values/general");
|
|
10
11
|
const r_value_1 = require("../dataflow/eval/values/r-value");
|
|
11
12
|
const simple_visitor_1 = require("./simple-visitor");
|
|
13
|
+
const convert_values_1 = require("../r-bridge/lang-4.x/convert-values");
|
|
12
14
|
class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor {
|
|
13
15
|
cachedConditions = new Map();
|
|
14
16
|
cachedStatements = new Map();
|
|
@@ -43,20 +45,21 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
|
|
|
43
45
|
const cfg = this.config.controlFlow.graph;
|
|
44
46
|
for (const [from, targets] of cfg.edges()) {
|
|
45
47
|
for (const [target, edge] of targets) {
|
|
46
|
-
if (
|
|
47
|
-
const og = this.getValue(edge
|
|
48
|
-
|
|
48
|
+
if (control_flow_graph_1.CfgEdge.isControlDependency(edge)) {
|
|
49
|
+
const og = this.getValue(control_flow_graph_1.CfgEdge.unpackCause(edge));
|
|
50
|
+
const w = control_flow_graph_1.CfgEdge.unpackWhen(edge);
|
|
51
|
+
if (og === logic_1.Ternary.Always && w === convert_values_1.RFalse) {
|
|
49
52
|
cfg.removeEdge(from, target);
|
|
50
53
|
}
|
|
51
|
-
else if (og === logic_1.Ternary.Never &&
|
|
54
|
+
else if (og === logic_1.Ternary.Never && w === convert_values_1.RTrue) {
|
|
52
55
|
cfg.removeEdge(from, target);
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
|
-
else if (
|
|
58
|
+
else if (control_flow_graph_1.CfgEdge.isFlowDependency(edge) && this.isUnconditionalJump(target)) {
|
|
56
59
|
// for each unconditional jump, we find the corresponding end/exit nodes and remove any flow edges
|
|
57
|
-
for (const end of this.getCfgVertex(target)
|
|
60
|
+
for (const end of control_flow_graph_1.CfgVertex.getEnd(this.getCfgVertex(target)) ?? []) {
|
|
58
61
|
for (const [target, edge] of cfg.ingoingEdges(end) ?? []) {
|
|
59
|
-
if (
|
|
62
|
+
if (control_flow_graph_1.CfgEdge.isFlowDependency(edge)) {
|
|
60
63
|
cfg.removeEdge(target, end);
|
|
61
64
|
}
|
|
62
65
|
}
|
|
@@ -132,8 +135,8 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
|
|
|
132
135
|
if (!body) {
|
|
133
136
|
return;
|
|
134
137
|
}
|
|
135
|
-
(0, simple_visitor_1.visitCfgInOrder)(this.config.controlFlow.graph, [body
|
|
136
|
-
if (body
|
|
138
|
+
(0, simple_visitor_1.visitCfgInOrder)(this.config.controlFlow.graph, [control_flow_graph_1.CfgVertex.getId(body)], n => {
|
|
139
|
+
if (control_flow_graph_1.CfgVertex.getEnd(body)?.includes(n)) {
|
|
137
140
|
return true;
|
|
138
141
|
}
|
|
139
142
|
this.inTry.add(n);
|
|
@@ -7,8 +7,8 @@ declare const CfgProperties: {
|
|
|
7
7
|
readonly 'has-entry-and-exit': typeof hasEntryAndExit;
|
|
8
8
|
readonly 'entry-reaches-all': typeof checkEntryReachesAll;
|
|
9
9
|
readonly 'exit-reaches-all': typeof checkExitIsReachedByAll;
|
|
10
|
-
readonly 'no-direct-fd-cycles': (c: ControlFlowInformation<import("./control-flow-graph").
|
|
11
|
-
readonly 'no-direct-cd-cycles': (c: ControlFlowInformation<import("./control-flow-graph").
|
|
10
|
+
readonly 'no-direct-fd-cycles': (c: ControlFlowInformation<import("./control-flow-graph").CfgVertex>) => boolean;
|
|
11
|
+
readonly 'no-direct-cd-cycles': (c: ControlFlowInformation<import("./control-flow-graph").CfgVertex>) => boolean;
|
|
12
12
|
};
|
|
13
13
|
export type CfgProperty = keyof typeof CfgProperties;
|
|
14
14
|
declare function checkSingleEntryAndExit(cfg: ControlFlowInformation): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.assertCfgSatisfiesProperties = assertCfgSatisfiesProperties;
|
|
4
|
+
const control_flow_graph_1 = require("./control-flow-graph");
|
|
4
5
|
const set_1 = require("../util/collections/set");
|
|
5
6
|
const log_1 = require("../util/log");
|
|
6
7
|
const simple_visitor_1 = require("./simple-visitor");
|
|
@@ -56,7 +57,7 @@ function _checkFdIOCount(cfg, dir, type, limit) {
|
|
|
56
57
|
for (const [from, targets] of cfg.graph.edges()) {
|
|
57
58
|
for (const [to, edge] of targets) {
|
|
58
59
|
const important = dir === 'in' ? to : from;
|
|
59
|
-
if (
|
|
60
|
+
if (control_flow_graph_1.CfgEdge.isFlowDependency(edge)) {
|
|
60
61
|
counts.set(important, (counts.get(important) ?? 0) + 1);
|
|
61
62
|
}
|
|
62
63
|
}
|
|
@@ -76,7 +77,7 @@ function _checkFdIOCount(cfg, dir, type, limit) {
|
|
|
76
77
|
function checkNoDirectCycles(cfg, type) {
|
|
77
78
|
for (const [from, targets] of cfg.graph.edges()) {
|
|
78
79
|
for (const [to, edge] of targets) {
|
|
79
|
-
if (edge
|
|
80
|
+
if (control_flow_graph_1.CfgEdge.isOfType(edge, type) && to === from) {
|
|
80
81
|
log_1.log.error(`Node ${from} has a direct cycle with ${to}`);
|
|
81
82
|
return false;
|
|
82
83
|
}
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.convertCfgToBasicBlocks = convertCfgToBasicBlocks;
|
|
4
4
|
const control_flow_graph_1 = require("./control-flow-graph");
|
|
5
|
+
const assert_1 = require("../util/assert");
|
|
5
6
|
/** if true, return the target */
|
|
6
7
|
function singleOutgoingFd(outgoing) {
|
|
7
8
|
if (!outgoing || outgoing.size !== 1) {
|
|
8
9
|
return undefined;
|
|
9
10
|
}
|
|
10
11
|
const next = outgoing.entries().next().value;
|
|
11
|
-
if (next?.[1]
|
|
12
|
+
if (control_flow_graph_1.CfgEdge.isFlowDependency(next?.[1])) {
|
|
12
13
|
return next[0];
|
|
13
14
|
}
|
|
14
15
|
else {
|
|
@@ -24,7 +25,7 @@ function convertCfgToBasicBlocks(cfInfo) {
|
|
|
24
25
|
return cfInfo;
|
|
25
26
|
}
|
|
26
27
|
for (const [id, vtx] of newCfg.vertices(false)) {
|
|
27
|
-
if (
|
|
28
|
+
if (!control_flow_graph_1.CfgVertex.isBlock(vtx)) {
|
|
28
29
|
continue;
|
|
29
30
|
}
|
|
30
31
|
const outgoing = newCfg.outgoingEdges(id);
|
|
@@ -44,9 +45,9 @@ function convertCfgToBasicBlocks(cfInfo) {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
|
-
const findEntries = cfInfo.entryPoints.map(e => newCfg.getBasicBlock(e)
|
|
48
|
-
const findExits = cfInfo.exitPoints.map(e => newCfg.getBasicBlock(e)
|
|
49
|
-
if (findEntries.some(
|
|
48
|
+
const findEntries = cfInfo.entryPoints.map(e => control_flow_graph_1.CfgVertex.getId(newCfg.getBasicBlock(e)));
|
|
49
|
+
const findExits = cfInfo.exitPoints.map(e => control_flow_graph_1.CfgVertex.getId(newCfg.getBasicBlock(e)));
|
|
50
|
+
if (findEntries.some(assert_1.isUndefined) || findExits.some(assert_1.isUndefined)) {
|
|
50
51
|
/* something went wrong */
|
|
51
52
|
return cfInfo;
|
|
52
53
|
}
|
|
@@ -61,19 +62,15 @@ function convertCfgToBasicBlocks(cfInfo) {
|
|
|
61
62
|
function wrapEveryVertexInBasicBlock(existing) {
|
|
62
63
|
const newGraph = new control_flow_graph_1.ControlFlowGraph();
|
|
63
64
|
for (const [id, vertex] of existing.vertices(false)) {
|
|
64
|
-
if (
|
|
65
|
+
if (control_flow_graph_1.CfgVertex.isBlock(vertex)) {
|
|
65
66
|
return undefined;
|
|
66
67
|
}
|
|
67
|
-
newGraph.addVertex(
|
|
68
|
-
type: control_flow_graph_1.CfgVertexType.Block,
|
|
69
|
-
elems: [vertex],
|
|
70
|
-
id: 'bb-' + id,
|
|
71
|
-
});
|
|
68
|
+
newGraph.addVertex(control_flow_graph_1.CfgVertex.makeBlock(control_flow_graph_1.CfgVertex.toBasicBlockId(id), [vertex]));
|
|
72
69
|
}
|
|
73
70
|
// promote all edges
|
|
74
71
|
for (const [from, outgoing] of existing.edges().entries()) {
|
|
75
72
|
for (const [to, edge] of outgoing.entries()) {
|
|
76
|
-
newGraph.addEdge(
|
|
73
|
+
newGraph.addEdge(control_flow_graph_1.CfgVertex.toBasicBlockId(from), control_flow_graph_1.CfgVertex.toBasicBlockId(to), edge);
|
|
77
74
|
}
|
|
78
75
|
}
|
|
79
76
|
return newGraph;
|