@eagleoutice/flowr 2.8.8 → 2.8.10
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 +19 -19
- package/abstract-interpretation/absint-visitor.d.ts +2 -2
- package/abstract-interpretation/absint-visitor.js +14 -12
- package/control-flow/cfg-dead-code.js +38 -21
- package/control-flow/semantic-cfg-guided-visitor.d.ts +38 -4
- package/control-flow/semantic-cfg-guided-visitor.js +46 -13
- package/control-flow/useless-loop.js +17 -20
- package/dataflow/environments/built-in-config.d.ts +1 -1
- package/dataflow/environments/built-in.js +2 -2
- package/dataflow/environments/default-builtin-config.d.ts +3 -3
- package/dataflow/environments/default-builtin-config.js +3 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.js +78 -1
- package/documentation/wiki-interface.js +1 -1
- package/linter/linter-format.d.ts +2 -2
- package/linter/linter-rules.d.ts +1 -5
- package/linter/rules/dead-code.d.ts +3 -7
- package/linter/rules/dead-code.js +25 -7
- package/package.json +1 -1
- package/project/plugins/file-plugins/files/flowr-namespace-file.js +4 -3
- package/queries/catalog/dependencies-query/dependencies-query-executor.js +5 -2
- package/queries/catalog/dependencies-query/dependencies-query-format.js +1 -1
- package/queries/catalog/linter-query/linter-query-format.js +1 -1
- package/r-bridge/lang-4.x/ast/model/processing/decorate.d.ts +6 -1
- package/r-bridge/lang-4.x/ast/model/processing/decorate.js +6 -2
- package/util/mermaid/cfg.d.ts +6 -1
- package/util/mermaid/cfg.js +11 -2
- 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.8.
|
|
27
|
+
flowR repl using flowR v2.8.9, R grammar v14 (tree-sitter engine)
|
|
28
28
|
R> :query @linter "read.csv(\"/root/x.txt\")"
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -35,11 +35,11 @@ It offers a wide variety of features, for example:
|
|
|
35
35
|
```text
|
|
36
36
|
Query: linter (2 ms)
|
|
37
37
|
╰ Deprecated Functions (deprecated-functions):
|
|
38
|
-
╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs:
|
|
38
|
+
╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, 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: 0, processTimeMs:
|
|
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):
|
|
@@ -51,14 +51,14 @@ It offers a wide variety of features, for example:
|
|
|
51
51
|
╰ Naming Convention (naming-convention):
|
|
52
52
|
╰ Metadata: numMatches: 0, numBreak: 0, searchTimeMs: 0, processTimeMs: 0
|
|
53
53
|
╰ Network Functions (network-functions):
|
|
54
|
-
╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs:
|
|
54
|
+
╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0
|
|
55
55
|
╰ Dataframe Access Validation (dataframe-access-validation):
|
|
56
56
|
╰ Metadata: numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 0
|
|
57
57
|
╰ Dead Code (dead-code):
|
|
58
58
|
╰ Metadata: consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0
|
|
59
59
|
╰ Useless Loops (useless-loop):
|
|
60
|
-
╰ Metadata: numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs:
|
|
61
|
-
All queries together required ≈2 ms (1ms accuracy, total
|
|
60
|
+
╰ Metadata: numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0
|
|
61
|
+
All queries together required ≈2 ms (1ms accuracy, total 2 ms)
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
|
|
@@ -82,17 +82,17 @@ It offers a wide variety of features, for example:
|
|
|
82
82
|
|
|
83
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: 0, processTimeMs:
|
|
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):\
|
|
93
93
|
╰ certain:\
|
|
94
94
|
╰ Path `/root/x.txt` at 1.1-23\
|
|
95
|
-
╰ _Metadata_: <code>totalConsidered: 1, totalUnknown: 0, searchTimeMs:
|
|
95
|
+
╰ _Metadata_: <code>totalConsidered: 1, totalUnknown: 0, searchTimeMs: 1, processTimeMs: 0</code>\
|
|
96
96
|
╰ **Unused Definitions** (unused-definitions):\
|
|
97
97
|
╰ _Metadata_: <code>totalConsidered: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
98
98
|
╰ **Naming Convention** (naming-convention):\
|
|
@@ -102,14 +102,14 @@ It offers a wide variety of features, for example:
|
|
|
102
102
|
╰ **Dataframe Access Validation** (dataframe-access-validation):\
|
|
103
103
|
╰ _Metadata_: <code>numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
104
104
|
╰ **Dead Code** (dead-code):\
|
|
105
|
-
╰ _Metadata_: <code>consideredNodes: 5, searchTimeMs:
|
|
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
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 _2.
|
|
112
|
+
The analysis required _2.2 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
|
},
|
|
@@ -150,7 +150,7 @@ It offers a wide variety of features, for example:
|
|
|
150
150
|
"totalWritesBeforeAlways": 0,
|
|
151
151
|
"totalValid": 0,
|
|
152
152
|
"searchTimeMs": 0,
|
|
153
|
-
"processTimeMs":
|
|
153
|
+
"processTimeMs": 0
|
|
154
154
|
}
|
|
155
155
|
},
|
|
156
156
|
"seeded-randomness": {
|
|
@@ -181,7 +181,7 @@ It offers a wide variety of features, for example:
|
|
|
181
181
|
".meta": {
|
|
182
182
|
"totalConsidered": 1,
|
|
183
183
|
"totalUnknown": 0,
|
|
184
|
-
"searchTimeMs":
|
|
184
|
+
"searchTimeMs": 1,
|
|
185
185
|
"processTimeMs": 0
|
|
186
186
|
}
|
|
187
187
|
},
|
|
@@ -225,7 +225,7 @@ It offers a wide variety of features, for example:
|
|
|
225
225
|
"results": [],
|
|
226
226
|
".meta": {
|
|
227
227
|
"consideredNodes": 5,
|
|
228
|
-
"searchTimeMs":
|
|
228
|
+
"searchTimeMs": 0,
|
|
229
229
|
"processTimeMs": 0
|
|
230
230
|
}
|
|
231
231
|
},
|
|
@@ -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.8.
|
|
311
|
+
flowR repl using flowR v2.8.9, 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!">117 ms</span></i> (as of Jan 18, 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/dataflow-graph) for more details on the dataflow graphs as well as call graphs.
|
|
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.8.
|
|
395
|
+
flowR repl using flowR v2.8.9, 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 _2.
|
|
700
|
+
(The analysis required _2.0 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,7 +1,7 @@
|
|
|
1
1
|
import type { CfgSimpleVertex, ControlFlowInformation } from '../control-flow/control-flow-graph';
|
|
2
2
|
import type { SemanticCfgGuidedVisitorConfiguration } from '../control-flow/semantic-cfg-guided-visitor';
|
|
3
3
|
import { SemanticCfgGuidedVisitor } from '../control-flow/semantic-cfg-guided-visitor';
|
|
4
|
-
import {
|
|
4
|
+
import { BuiltInProcName } from '../dataflow/environments/built-in';
|
|
5
5
|
import type { DataflowGraph } from '../dataflow/graph/graph';
|
|
6
6
|
import { type DataflowGraphVertexFunctionCall, type DataflowGraphVertexVariableDefinition } from '../dataflow/graph/vertex';
|
|
7
7
|
import type { NoInfo, RNode } from '../r-bridge/lang-4.x/ast/model/model';
|
|
@@ -62,7 +62,7 @@ export declare abstract class AbstractInterpretationVisitor<Domain extends AnyAb
|
|
|
62
62
|
getAbstractTrace(): ReadonlyMap<NodeId, StateAbstractDomain<Domain>>;
|
|
63
63
|
start(): void;
|
|
64
64
|
protected visitNode(vertexId: NodeId): boolean;
|
|
65
|
-
protected onDispatchFunctionCallOrigin(call: DataflowGraphVertexFunctionCall, origin:
|
|
65
|
+
protected onDispatchFunctionCallOrigin(call: DataflowGraphVertexFunctionCall, origin: BuiltInProcName): void;
|
|
66
66
|
protected onVariableDefinition({ vertex }: {
|
|
67
67
|
vertex: DataflowGraphVertexVariableDefinition;
|
|
68
68
|
}): void;
|
|
@@ -151,19 +151,21 @@ class AbstractInterpretationVisitor extends semantic_cfg_guided_visitor_1.Semant
|
|
|
151
151
|
}
|
|
152
152
|
onDispatchFunctionCallOrigin(call, origin) {
|
|
153
153
|
super.onDispatchFunctionCallOrigin(call, origin);
|
|
154
|
-
if (!(origin in built_in_1.BuiltInProcessorMapper)) {
|
|
155
|
-
return this.onFunctionCall({ call });
|
|
156
|
-
}
|
|
157
154
|
switch (origin) {
|
|
158
|
-
case
|
|
159
|
-
case
|
|
160
|
-
case
|
|
161
|
-
case
|
|
162
|
-
case
|
|
163
|
-
case
|
|
164
|
-
case
|
|
165
|
-
case
|
|
166
|
-
case
|
|
155
|
+
case built_in_1.BuiltInProcName.ExpressionList:
|
|
156
|
+
case built_in_1.BuiltInProcName.IfThenElse:
|
|
157
|
+
case built_in_1.BuiltInProcName.ForLoop:
|
|
158
|
+
case built_in_1.BuiltInProcName.WhileLoop:
|
|
159
|
+
case built_in_1.BuiltInProcName.RepeatLoop:
|
|
160
|
+
case built_in_1.BuiltInProcName.FunctionDefinition:
|
|
161
|
+
case built_in_1.BuiltInProcName.Assignment:
|
|
162
|
+
case built_in_1.BuiltInProcName.AssignmentLike:
|
|
163
|
+
case built_in_1.BuiltInProcName.TableAssignment:
|
|
164
|
+
case built_in_1.BuiltInProcName.Replacement:
|
|
165
|
+
case built_in_1.BuiltInProcName.Access:
|
|
166
|
+
case built_in_1.BuiltInProcName.Pipe:
|
|
167
|
+
case built_in_1.BuiltInProcName.Break:
|
|
168
|
+
case built_in_1.BuiltInProcName.Return:
|
|
167
169
|
return;
|
|
168
170
|
default:
|
|
169
171
|
return this.onFunctionCall({ call });
|
|
@@ -8,9 +8,11 @@ const log_1 = require("../util/log");
|
|
|
8
8
|
const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
9
9
|
const general_1 = require("../dataflow/eval/values/general");
|
|
10
10
|
const r_value_1 = require("../dataflow/eval/values/r-value");
|
|
11
|
+
const simple_visitor_1 = require("./simple-visitor");
|
|
11
12
|
class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor {
|
|
12
13
|
cachedConditions = new Map();
|
|
13
14
|
cachedStatements = new Map();
|
|
15
|
+
inTry = new Set();
|
|
14
16
|
getValue(id) {
|
|
15
17
|
const has = this.cachedConditions.get(id);
|
|
16
18
|
if (has) {
|
|
@@ -20,6 +22,9 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
|
|
|
20
22
|
return this.cachedConditions.get(id) ?? logic_1.Ternary.Maybe;
|
|
21
23
|
}
|
|
22
24
|
isUnconditionalJump(id) {
|
|
25
|
+
if (this.inTry.has(id)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
23
28
|
const has = this.cachedStatements.get(id);
|
|
24
29
|
if (has) {
|
|
25
30
|
return has;
|
|
@@ -45,9 +50,14 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
|
|
|
45
50
|
this.config.controlFlow.graph.removeEdge(from, target);
|
|
46
51
|
}
|
|
47
52
|
}
|
|
48
|
-
else if (edge.label === 0 /* CfgEdgeType.Fd */) {
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
else if (edge.label === 0 /* CfgEdgeType.Fd */ && this.isUnconditionalJump(target)) {
|
|
54
|
+
// for each unconditional jump, we find the corresponding end/exit nodes and remove any flow edges
|
|
55
|
+
for (const end of this.getCfgVertex(target)?.end ?? []) {
|
|
56
|
+
for (const [target, edge] of this.config.controlFlow.graph.ingoingEdges(end) ?? []) {
|
|
57
|
+
if (edge.label === 0 /* CfgEdgeType.Fd */) {
|
|
58
|
+
this.config.controlFlow.graph.removeEdge(target, end);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
51
61
|
}
|
|
52
62
|
}
|
|
53
63
|
}
|
|
@@ -91,32 +101,39 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
|
|
|
91
101
|
}
|
|
92
102
|
return Boolean(values.elements[0].value);
|
|
93
103
|
}
|
|
94
|
-
handleFunctionCall(data) {
|
|
95
|
-
switch (data.call.origin[0]) {
|
|
96
|
-
case 'builtin:return':
|
|
97
|
-
case 'builtin:stop':
|
|
98
|
-
this.cachedStatements.set(data.call.id, true);
|
|
99
|
-
break;
|
|
100
|
-
case 'builtin:stopifnot': {
|
|
101
|
-
const arg = this.getBoolArgValue(data);
|
|
102
|
-
if (arg !== undefined) {
|
|
103
|
-
this.cachedStatements.set(data.call.id, !arg);
|
|
104
|
-
}
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
104
|
onIfThenElseCall(data) {
|
|
110
105
|
this.handleWithCondition(data);
|
|
111
106
|
}
|
|
112
107
|
onWhileLoopCall(data) {
|
|
113
108
|
this.handleWithCondition(data);
|
|
114
109
|
}
|
|
115
|
-
|
|
116
|
-
this.
|
|
110
|
+
onReturnCall(data) {
|
|
111
|
+
this.cachedStatements.set(data.call.id, true);
|
|
112
|
+
}
|
|
113
|
+
onStopCall(data) {
|
|
114
|
+
this.cachedStatements.set(data.call.id, true);
|
|
117
115
|
}
|
|
118
116
|
onStopIfNotCall(data) {
|
|
119
|
-
this.
|
|
117
|
+
const arg = this.getBoolArgValue(data);
|
|
118
|
+
if (arg !== undefined) {
|
|
119
|
+
this.cachedStatements.set(data.call.id, !arg);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
onTryCall(data) {
|
|
123
|
+
if (data.call.args.length < 1 || data.call.args[0] === r_function_call_1.EmptyArgument) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const body = this.getCfgVertex(data.call.args[0].nodeId);
|
|
127
|
+
if (!body) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
(0, simple_visitor_1.visitCfgInOrder)(this.config.controlFlow.graph, [body.id], n => {
|
|
131
|
+
if (body.end?.includes(n)) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
this.inTry.add(n);
|
|
135
|
+
return false;
|
|
136
|
+
});
|
|
120
137
|
}
|
|
121
138
|
}
|
|
122
139
|
/** Breaks unsatisfiable control dependencies */
|
|
@@ -11,9 +11,10 @@ import type { RLogical } from '../r-bridge/lang-4.x/ast/model/nodes/r-logical';
|
|
|
11
11
|
import type { DataflowGraph, FunctionArgument } from '../dataflow/graph/graph';
|
|
12
12
|
import type { NoInfo, RNode } from '../r-bridge/lang-4.x/ast/model/model';
|
|
13
13
|
import type { RSymbol } from '../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
|
|
14
|
-
import {
|
|
14
|
+
import { BuiltInProcName } from '../dataflow/environments/built-in';
|
|
15
15
|
import type { RExpressionList } from '../r-bridge/lang-4.x/ast/model/nodes/r-expression-list';
|
|
16
16
|
import type { ReadOnlyFlowrAnalyzerContext } from '../project/context/flowr-analyzer-context';
|
|
17
|
+
import { RNull } from '../r-bridge/lang-4.x/convert-values';
|
|
17
18
|
export interface SemanticCfgGuidedVisitorConfiguration<OtherInfo = NoInfo, ControlFlow extends ControlFlowInformation = ControlFlowInformation, Ast extends NormalizedAst<OtherInfo> = NormalizedAst<OtherInfo>, Dfg extends DataflowGraph = DataflowGraph> extends DataflowCfgGuidedVisitorConfiguration<ControlFlow, Dfg>, SyntaxCfgGuidedVisitorConfiguration<OtherInfo, ControlFlow, Ast> {
|
|
18
19
|
readonly ctx: ReadOnlyFlowrAnalyzerContext;
|
|
19
20
|
}
|
|
@@ -100,7 +101,7 @@ export declare class SemanticCfgGuidedVisitor<OtherInfo = NoInfo, ControlFlow ex
|
|
|
100
101
|
* with the logic you desire.
|
|
101
102
|
* @protected
|
|
102
103
|
*/
|
|
103
|
-
protected onDispatchFunctionCallOrigins(call: DataflowGraphVertexFunctionCall, origins: readonly
|
|
104
|
+
protected onDispatchFunctionCallOrigins(call: DataflowGraphVertexFunctionCall, origins: readonly BuiltInProcName[]): void;
|
|
104
105
|
/**
|
|
105
106
|
* This function is responsible for dispatching the appropriate event
|
|
106
107
|
* based on a given dataflow vertex. The default serves as a backend
|
|
@@ -108,7 +109,7 @@ export declare class SemanticCfgGuidedVisitor<OtherInfo = NoInfo, ControlFlow ex
|
|
|
108
109
|
* @see {@link onDispatchFunctionCallOrigins} for the aggregation in case the function call target is ambiguous.
|
|
109
110
|
* @protected
|
|
110
111
|
*/
|
|
111
|
-
protected onDispatchFunctionCallOrigin(call: DataflowGraphVertexFunctionCall, origin:
|
|
112
|
+
protected onDispatchFunctionCallOrigin(call: DataflowGraphVertexFunctionCall, origin: BuiltInProcName): void;
|
|
112
113
|
/**
|
|
113
114
|
* This event is called for the root program node, i.e., the program that is being analyzed.
|
|
114
115
|
* @protected
|
|
@@ -125,7 +126,7 @@ export declare class SemanticCfgGuidedVisitor<OtherInfo = NoInfo, ControlFlow ex
|
|
|
125
126
|
*/
|
|
126
127
|
protected onNullConstant(_data: {
|
|
127
128
|
vertex: DataflowGraphVertexValue;
|
|
128
|
-
node: RSymbol<OtherInfo & ParentInformation,
|
|
129
|
+
node: RSymbol<OtherInfo & ParentInformation, typeof RNull>;
|
|
129
130
|
}): void;
|
|
130
131
|
/**
|
|
131
132
|
* Called for every constant string value in the program.
|
|
@@ -447,6 +448,17 @@ export declare class SemanticCfgGuidedVisitor<OtherInfo = NoInfo, ControlFlow ex
|
|
|
447
448
|
protected onVectorCall(_data: {
|
|
448
449
|
call: DataflowGraphVertexFunctionCall;
|
|
449
450
|
}): void;
|
|
451
|
+
/**
|
|
452
|
+
* This event triggers for every call to the `stop` function.
|
|
453
|
+
*
|
|
454
|
+
* For example, this triggers for `stop` in `stop()`.
|
|
455
|
+
*
|
|
456
|
+
* More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
|
|
457
|
+
* @protected
|
|
458
|
+
*/
|
|
459
|
+
protected onStopCall(_data: {
|
|
460
|
+
call: DataflowGraphVertexFunctionCall;
|
|
461
|
+
}): void;
|
|
450
462
|
/**
|
|
451
463
|
* This event triggers for every call to the `stopifnot` function.
|
|
452
464
|
*
|
|
@@ -500,4 +512,26 @@ export declare class SemanticCfgGuidedVisitor<OtherInfo = NoInfo, ControlFlow ex
|
|
|
500
512
|
protected onRegisterHookCall(_data: {
|
|
501
513
|
call: DataflowGraphVertexFunctionCall;
|
|
502
514
|
}): void;
|
|
515
|
+
/**
|
|
516
|
+
* This event triggers for every call to `break` to exit a loop.
|
|
517
|
+
*
|
|
518
|
+
* For example, this triggers for `break` in `repeat { break }`.
|
|
519
|
+
*
|
|
520
|
+
* More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
|
|
521
|
+
* @protected
|
|
522
|
+
*/
|
|
523
|
+
protected onBreakCall(_data: {
|
|
524
|
+
call: DataflowGraphVertexFunctionCall;
|
|
525
|
+
}): void;
|
|
526
|
+
/**
|
|
527
|
+
* This event triggers for every call to `return` to explicitly return a value in a function.
|
|
528
|
+
*
|
|
529
|
+
* For example, this triggers for `return` in `f <- function() { return(42) }`.
|
|
530
|
+
*
|
|
531
|
+
* More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
|
|
532
|
+
* @protected
|
|
533
|
+
*/
|
|
534
|
+
protected onReturnCall(_data: {
|
|
535
|
+
call: DataflowGraphVertexFunctionCall;
|
|
536
|
+
}): void;
|
|
503
537
|
}
|
|
@@ -8,6 +8,7 @@ const edge_1 = require("../dataflow/graph/edge");
|
|
|
8
8
|
const assert_1 = require("../util/assert");
|
|
9
9
|
const built_in_1 = require("../dataflow/environments/built-in");
|
|
10
10
|
const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
11
|
+
const convert_values_1 = require("../r-bridge/lang-4.x/convert-values");
|
|
11
12
|
/**
|
|
12
13
|
* This visitor extends on the {@link DataflowAwareCfgGuidedVisitor} by dispatching visitors for separate function calls as well,
|
|
13
14
|
* providing more information!
|
|
@@ -53,7 +54,7 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
|
|
|
53
54
|
case type_1.RType.Number: return this.onNumberConstant({ vertex: val, node: astNode });
|
|
54
55
|
case type_1.RType.Logical: return this.onLogicalConstant({ vertex: val, node: astNode });
|
|
55
56
|
case type_1.RType.Symbol:
|
|
56
|
-
if (astNode.lexeme ===
|
|
57
|
+
if (astNode.lexeme === convert_values_1.RNull) {
|
|
57
58
|
return this.onNullConstant({
|
|
58
59
|
vertex: val,
|
|
59
60
|
node: astNode
|
|
@@ -115,7 +116,7 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
|
|
|
115
116
|
*/
|
|
116
117
|
visitFunctionCall(vertex) {
|
|
117
118
|
super.visitFunctionCall(vertex);
|
|
118
|
-
if (vertex.origin ===
|
|
119
|
+
if (vertex.origin === built_in_1.BuiltInProcName.Unnamed) {
|
|
119
120
|
this.onUnnamedCall({ call: vertex });
|
|
120
121
|
}
|
|
121
122
|
else {
|
|
@@ -157,11 +158,7 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
|
|
|
157
158
|
* @protected
|
|
158
159
|
*/
|
|
159
160
|
onDispatchFunctionCallOrigin(call, origin) {
|
|
160
|
-
|
|
161
|
-
return this.onDefaultFunctionCall({ call });
|
|
162
|
-
}
|
|
163
|
-
const type = origin;
|
|
164
|
-
switch (type) {
|
|
161
|
+
switch (origin) {
|
|
165
162
|
case built_in_1.BuiltInProcName.Eval:
|
|
166
163
|
return this.onEvalFunctionCall({ call });
|
|
167
164
|
case built_in_1.BuiltInProcName.Apply:
|
|
@@ -201,7 +198,8 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
|
|
|
201
198
|
case built_in_1.BuiltInProcName.Vector:
|
|
202
199
|
return this.onVectorCall({ call });
|
|
203
200
|
case built_in_1.BuiltInProcName.Assignment:
|
|
204
|
-
case built_in_1.BuiltInProcName.AssignmentLike:
|
|
201
|
+
case built_in_1.BuiltInProcName.AssignmentLike:
|
|
202
|
+
case built_in_1.BuiltInProcName.TableAssignment: {
|
|
205
203
|
const outgoing = this.config.dfg.outgoingEdges(call.id);
|
|
206
204
|
if (outgoing) {
|
|
207
205
|
const target = [...outgoing.entries()].filter(([, e]) => (0, edge_1.edgeIncludesType)(e.types, edge_1.EdgeType.Returns));
|
|
@@ -253,22 +251,30 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
|
|
|
253
251
|
}
|
|
254
252
|
case built_in_1.BuiltInProcName.Library:
|
|
255
253
|
return this.onLibraryCall({ call });
|
|
256
|
-
case built_in_1.BuiltInProcName.Default:
|
|
257
|
-
return this.onDefaultFunctionCall({ call });
|
|
258
254
|
case built_in_1.BuiltInProcName.Try:
|
|
259
255
|
return this.onTryCall({ call });
|
|
256
|
+
case built_in_1.BuiltInProcName.Stop:
|
|
257
|
+
return this.onStopCall({ call });
|
|
260
258
|
case built_in_1.BuiltInProcName.StopIfNot:
|
|
261
259
|
return this.onStopIfNotCall({ call });
|
|
262
260
|
case built_in_1.BuiltInProcName.RegisterHook:
|
|
263
261
|
return this.onRegisterHookCall({ call });
|
|
264
262
|
case built_in_1.BuiltInProcName.Local:
|
|
265
263
|
return this.onLocalCall({ call });
|
|
266
|
-
case built_in_1.BuiltInProcName.FunctionDefinition:
|
|
267
|
-
throw new Error('Function call vertex cannot be a function definition');
|
|
268
264
|
case built_in_1.BuiltInProcName.S3Dispatch:
|
|
269
265
|
return this.onS3DispatchCall({ call });
|
|
266
|
+
case built_in_1.BuiltInProcName.Break:
|
|
267
|
+
return this.onBreakCall({ call });
|
|
268
|
+
case built_in_1.BuiltInProcName.Return:
|
|
269
|
+
return this.onReturnCall({ call });
|
|
270
|
+
case built_in_1.BuiltInProcName.Unnamed:
|
|
271
|
+
return this.onUnnamedCall({ call });
|
|
272
|
+
case built_in_1.BuiltInProcName.Default:
|
|
273
|
+
case built_in_1.BuiltInProcName.Function:
|
|
274
|
+
case built_in_1.BuiltInProcName.FunctionDefinition:
|
|
275
|
+
return this.onDefaultFunctionCall({ call });
|
|
270
276
|
default:
|
|
271
|
-
(0, assert_1.assertUnreachable)(
|
|
277
|
+
(0, assert_1.assertUnreachable)(origin);
|
|
272
278
|
}
|
|
273
279
|
}
|
|
274
280
|
/**
|
|
@@ -530,6 +536,15 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
|
|
|
530
536
|
* @protected
|
|
531
537
|
*/
|
|
532
538
|
onVectorCall(_data) { }
|
|
539
|
+
/**
|
|
540
|
+
* This event triggers for every call to the `stop` function.
|
|
541
|
+
*
|
|
542
|
+
* For example, this triggers for `stop` in `stop()`.
|
|
543
|
+
*
|
|
544
|
+
* More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
|
|
545
|
+
* @protected
|
|
546
|
+
*/
|
|
547
|
+
onStopCall(_data) { }
|
|
533
548
|
/**
|
|
534
549
|
* This event triggers for every call to the `stopifnot` function.
|
|
535
550
|
*
|
|
@@ -573,6 +588,24 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
|
|
|
573
588
|
* @protected
|
|
574
589
|
*/
|
|
575
590
|
onRegisterHookCall(_data) { }
|
|
591
|
+
/**
|
|
592
|
+
* This event triggers for every call to `break` to exit a loop.
|
|
593
|
+
*
|
|
594
|
+
* For example, this triggers for `break` in `repeat { break }`.
|
|
595
|
+
*
|
|
596
|
+
* More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
|
|
597
|
+
* @protected
|
|
598
|
+
*/
|
|
599
|
+
onBreakCall(_data) { }
|
|
600
|
+
/**
|
|
601
|
+
* This event triggers for every call to `return` to explicitly return a value in a function.
|
|
602
|
+
*
|
|
603
|
+
* For example, this triggers for `return` in `f <- function() { return(42) }`.
|
|
604
|
+
*
|
|
605
|
+
* More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
|
|
606
|
+
* @protected
|
|
607
|
+
*/
|
|
608
|
+
onReturnCall(_data) { }
|
|
576
609
|
}
|
|
577
610
|
exports.SemanticCfgGuidedVisitor = SemanticCfgGuidedVisitor;
|
|
578
611
|
//# sourceMappingURL=semantic-cfg-guided-visitor.js.map
|
|
@@ -29,7 +29,7 @@ function onlyLoopsOnce(loop, dataflow, controlflow, ast, ctx) {
|
|
|
29
29
|
(0, assert_1.guard)(vertex.tag === vertex_1.VertexType.FunctionCall, 'invalid vertex type for onlyLoopsOnce');
|
|
30
30
|
(0, assert_1.guard)(vertex.origin !== 'unnamed' && exports.loopyFunctions.has(vertex.origin[0]), 'onlyLoopsOnce can only be called with loops');
|
|
31
31
|
// 1. In case of for loop, check if vector has only one element
|
|
32
|
-
if (vertex.origin[0] ===
|
|
32
|
+
if (vertex.origin[0] === built_in_1.BuiltInProcName.ForLoop) {
|
|
33
33
|
if (vertex.args.length < 2) {
|
|
34
34
|
return undefined;
|
|
35
35
|
}
|
|
@@ -120,28 +120,25 @@ class CfgSingleIterationLoopDetector extends semantic_cfg_guided_visitor_1.Seman
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.encounteredLoopBreaker = true;
|
|
127
|
-
this.app(data.call.cds);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
else if (origin === 'builtin:stopifnot') {
|
|
131
|
-
const arg = this.getBoolArgValue(data);
|
|
132
|
-
if (arg === false) {
|
|
133
|
-
this.encounteredLoopBreaker = true;
|
|
134
|
-
this.app(data.call.cds);
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
123
|
+
onBreakCall(data) {
|
|
124
|
+
this.encounteredLoopBreaker = true;
|
|
125
|
+
this.app(data.call.cds);
|
|
139
126
|
}
|
|
140
|
-
|
|
141
|
-
this.
|
|
127
|
+
onReturnCall(data) {
|
|
128
|
+
this.encounteredLoopBreaker = true;
|
|
129
|
+
this.app(data.call.cds);
|
|
130
|
+
}
|
|
131
|
+
onStopCall(data) {
|
|
132
|
+
this.encounteredLoopBreaker = true;
|
|
133
|
+
this.app(data.call.cds);
|
|
142
134
|
}
|
|
143
135
|
onStopIfNotCall(data) {
|
|
144
|
-
this.
|
|
136
|
+
const arg = this.getBoolArgValue(data);
|
|
137
|
+
if (arg === false) {
|
|
138
|
+
this.encounteredLoopBreaker = true;
|
|
139
|
+
this.app(data.call.cds);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
145
142
|
}
|
|
146
143
|
loopsOnlyOnce() {
|
|
147
144
|
this.startVisitor([]);
|
|
@@ -32,7 +32,7 @@ export interface BuiltInFunctionDefinition<BuiltInProcessor extends keyof typeof
|
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
34
|
* Define a built-in replacement (like `[` or `$`) and the processor to use.
|
|
35
|
-
* This is a convenience for manually combined function calls
|
|
35
|
+
* This is a convenience for manually combined replacement function calls.
|
|
36
36
|
*/
|
|
37
37
|
export interface BuiltInReplacementDefinition extends BaseBuiltInDefinition {
|
|
38
38
|
readonly type: 'replacement';
|
|
@@ -268,8 +268,8 @@ class BuiltIns {
|
|
|
268
268
|
* Registers all combinations of replacements
|
|
269
269
|
*/
|
|
270
270
|
registerReplacementFunctions({ names, suffixes, assumePrimitive, config }) {
|
|
271
|
-
const replacer = exports.BuiltInProcessorMapper[
|
|
272
|
-
(0, assert_1.guard)(replacer !== undefined, () =>
|
|
271
|
+
const replacer = exports.BuiltInProcessorMapper[BuiltInProcName.Replacement];
|
|
272
|
+
(0, assert_1.guard)(replacer !== undefined, () => `Processor for ${BuiltInProcName.Replacement} is undefined!`);
|
|
273
273
|
for (const assignment of names) {
|
|
274
274
|
for (const suffix of suffixes) {
|
|
275
275
|
const effectiveName = `${assignment}${suffix}`;
|
|
@@ -257,7 +257,7 @@ export declare const DefaultBuiltinConfig: [{
|
|
|
257
257
|
readonly config: {
|
|
258
258
|
readonly returnsNthArgument: 0;
|
|
259
259
|
readonly cfg: ExitPointType.Return;
|
|
260
|
-
readonly useAsProcessor:
|
|
260
|
+
readonly useAsProcessor: BuiltInProcName.Return;
|
|
261
261
|
};
|
|
262
262
|
readonly assumePrimitive: true;
|
|
263
263
|
}, {
|
|
@@ -302,7 +302,7 @@ export declare const DefaultBuiltinConfig: [{
|
|
|
302
302
|
readonly names: ["break"];
|
|
303
303
|
readonly processor: BuiltInProcName.Default;
|
|
304
304
|
readonly config: {
|
|
305
|
-
readonly useAsProcessor:
|
|
305
|
+
readonly useAsProcessor: BuiltInProcName.Break;
|
|
306
306
|
readonly cfg: ExitPointType.Break;
|
|
307
307
|
};
|
|
308
308
|
readonly assumePrimitive: false;
|
|
@@ -604,7 +604,7 @@ export declare const DefaultBuiltinConfig: [{
|
|
|
604
604
|
readonly processor: BuiltInProcName.Vector;
|
|
605
605
|
readonly config: {};
|
|
606
606
|
readonly assumePrimitive: true;
|
|
607
|
-
readonly evalHandler: "
|
|
607
|
+
readonly evalHandler: "built-in:c";
|
|
608
608
|
}, {
|
|
609
609
|
readonly type: "function";
|
|
610
610
|
readonly names: ["setnames", "setNames", "setkey", "setkeyv", "setindex", "setindexv", "setattr"];
|
|
@@ -232,7 +232,7 @@ exports.DefaultBuiltinConfig = [
|
|
|
232
232
|
{ type: 'function', names: ['eval'], processor: built_in_1.BuiltInProcName.Eval, config: { includeFunctionCall: true }, assumePrimitive: true },
|
|
233
233
|
{ type: 'function', names: ['cat'], processor: built_in_1.BuiltInProcName.Default, config: { forceArgs: 'all', hasUnknownSideEffects: { type: 'link-to-last-call', callName: /^sink$/ } }, assumePrimitive: false },
|
|
234
234
|
{ type: 'function', names: ['switch'], processor: built_in_1.BuiltInProcName.Default, config: { forceArgs: [true] }, assumePrimitive: false },
|
|
235
|
-
{ type: 'function', names: ['return'], processor: built_in_1.BuiltInProcName.Default, config: { returnsNthArgument: 0, cfg: 1 /* ExitPointType.Return */, useAsProcessor:
|
|
235
|
+
{ type: 'function', names: ['return'], processor: built_in_1.BuiltInProcName.Default, config: { returnsNthArgument: 0, cfg: 1 /* ExitPointType.Return */, useAsProcessor: built_in_1.BuiltInProcName.Return }, assumePrimitive: true },
|
|
236
236
|
{
|
|
237
237
|
type: 'function',
|
|
238
238
|
names: ['stop', 'abort', 'cli_abort', 'throw', 'stop_bad_type', 'stop_bad_element_type', 'stop_bad_element_length'],
|
|
@@ -243,7 +243,7 @@ exports.DefaultBuiltinConfig = [
|
|
|
243
243
|
{ type: 'function', names: ['try'], processor: built_in_1.BuiltInProcName.Try, config: { block: 'expr', handlers: {} }, assumePrimitive: true },
|
|
244
244
|
{ type: 'function', names: ['tryCatch', 'tryCatchLog'], processor: built_in_1.BuiltInProcName.Try, config: { block: 'expr', handlers: { error: 'error', finally: 'finally' } }, assumePrimitive: true },
|
|
245
245
|
{ type: 'function', names: ['stopifnot'], processor: built_in_1.BuiltInProcName.StopIfNot, config: {}, assumePrimitive: false },
|
|
246
|
-
{ type: 'function', names: ['break'], processor: built_in_1.BuiltInProcName.Default, config: { useAsProcessor:
|
|
246
|
+
{ type: 'function', names: ['break'], processor: built_in_1.BuiltInProcName.Default, config: { useAsProcessor: built_in_1.BuiltInProcName.Break, cfg: 2 /* ExitPointType.Break */ }, assumePrimitive: false },
|
|
247
247
|
{ type: 'function', names: ['next'], processor: built_in_1.BuiltInProcName.Default, config: { cfg: 3 /* ExitPointType.Next */ }, assumePrimitive: false },
|
|
248
248
|
{ type: 'function', names: ['{'], processor: built_in_1.BuiltInProcName.ExpressionList, config: {}, assumePrimitive: true },
|
|
249
249
|
{ type: 'function', names: ['source'], processor: built_in_1.BuiltInProcName.Source, config: { includeFunctionCall: true, forceFollow: false }, assumePrimitive: false },
|
|
@@ -278,7 +278,7 @@ exports.DefaultBuiltinConfig = [
|
|
|
278
278
|
{ type: 'function', names: ['interference'], processor: built_in_1.BuiltInProcName.Apply, config: { unquoteFunction: true, nameOfFunctionArgument: 'propensity_integrand', libFn: true }, assumePrimitive: false },
|
|
279
279
|
{ type: 'function', names: ['ddply'], processor: built_in_1.BuiltInProcName.Apply, config: { unquoteFunction: true, indexOfFunction: 2, nameOfFunctionArgument: '.fun', libFn: true }, assumePrimitive: false },
|
|
280
280
|
{ type: 'function', names: ['list'], processor: built_in_1.BuiltInProcName.List, config: {}, assumePrimitive: true },
|
|
281
|
-
{ type: 'function', names: ['c'], processor: built_in_1.BuiltInProcName.Vector, config: {}, assumePrimitive: true, evalHandler: '
|
|
281
|
+
{ type: 'function', names: ['c'], processor: built_in_1.BuiltInProcName.Vector, config: {}, assumePrimitive: true, evalHandler: 'built-in:c' },
|
|
282
282
|
{
|
|
283
283
|
type: 'function',
|
|
284
284
|
names: ['setnames', 'setNames', 'setkey', 'setkeyv', 'setindex', 'setindexv', 'setattr'],
|
|
@@ -56,7 +56,7 @@ function tryReplacementPassingIndices(rootId, functionName, data, name, args, in
|
|
|
56
56
|
if (resolved.length !== 1 || resolved[0].type !== identifier_1.ReferenceType.BuiltInFunction) {
|
|
57
57
|
return (0, process_named_call_1.processAsNamedCall)(functionName, data, name, args);
|
|
58
58
|
}
|
|
59
|
-
const info = built_in_1.BuiltInProcessorMapper[
|
|
59
|
+
const info = built_in_1.BuiltInProcessorMapper[built_in_1.BuiltInProcName.Replacement]({
|
|
60
60
|
type: type_1.RType.Symbol,
|
|
61
61
|
info: functionName.info,
|
|
62
62
|
content: name,
|
|
@@ -35,7 +35,7 @@ args, rootId, data, config) {
|
|
|
35
35
|
indices ??= constructAccessedIndices(name.content, args);
|
|
36
36
|
}
|
|
37
37
|
/* we assign the first argument by the last for now and maybe mark as maybe!, we can keep the symbol as we now know we have an assignment */
|
|
38
|
-
let res = built_in_1.BuiltInProcessorMapper[
|
|
38
|
+
let res = built_in_1.BuiltInProcessorMapper[built_in_1.BuiltInProcName.Assignment](name, [args[0], args.at(-1)], rootId, data, {
|
|
39
39
|
superAssignment: config.assignmentOperator === '<<-',
|
|
40
40
|
makeMaybe: indices === undefined ? config.makeMaybe : false,
|
|
41
41
|
indicesCollection: indices,
|
|
@@ -12,6 +12,10 @@ const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
|
|
|
12
12
|
const assert_1 = require("../../../../../../util/assert");
|
|
13
13
|
const edge_1 = require("../../../../../graph/edge");
|
|
14
14
|
const built_in_1 = require("../../../../../environments/built-in");
|
|
15
|
+
const unnamed_call_handling_1 = require("../unnamed-call-handling");
|
|
16
|
+
const identifier_1 = require("../../../../../environments/identifier");
|
|
17
|
+
const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
|
|
18
|
+
const log_1 = require("../../../../../../util/log");
|
|
15
19
|
function getArgsOfName(argMaps, name) {
|
|
16
20
|
return new Set(argMaps.entries().filter(([, v]) => v === name).map(([k]) => k));
|
|
17
21
|
}
|
|
@@ -72,7 +76,11 @@ function processTryCatch(name, args, rootId, data, config) {
|
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
78
|
for (const e of errorArg) {
|
|
75
|
-
info.graph.addEdge(rootId, e, edge_1.EdgeType.Calls);
|
|
79
|
+
info.graph.addEdge(rootId, e, edge_1.EdgeType.Reads | edge_1.EdgeType.Calls);
|
|
80
|
+
const linkTo = promoteCallToFunction(rootId, e, info, data);
|
|
81
|
+
if (linkTo) {
|
|
82
|
+
info.graph.addEdge(e, linkTo, edge_1.EdgeType.Calls);
|
|
83
|
+
}
|
|
76
84
|
}
|
|
77
85
|
for (const f of finallyArg) {
|
|
78
86
|
info.graph.addEdge(rootId, f, edge_1.EdgeType.Calls);
|
|
@@ -84,6 +92,75 @@ function processTryCatch(name, args, rootId, data, config) {
|
|
|
84
92
|
}
|
|
85
93
|
return info;
|
|
86
94
|
}
|
|
95
|
+
function promoteCallToFunction(call, arg, info, data) {
|
|
96
|
+
let functionId = undefined;
|
|
97
|
+
let functionName = undefined;
|
|
98
|
+
let anonymous = false;
|
|
99
|
+
const argNode = data.completeAst.idMap.get(arg);
|
|
100
|
+
if (!argNode) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
const val = argNode.type === type_1.RType.Argument ? (0, unpack_argument_1.unpackArg)(argNode) : argNode;
|
|
104
|
+
if (!val) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
if (val.type === type_1.RType.Symbol) {
|
|
108
|
+
functionId = val.info.id;
|
|
109
|
+
functionName = val.content;
|
|
110
|
+
}
|
|
111
|
+
else if (val.type === type_1.RType.FunctionDefinition) {
|
|
112
|
+
anonymous = true;
|
|
113
|
+
functionId = val.info.id;
|
|
114
|
+
functionName = `${unnamed_call_handling_1.UnnamedFunctionCallPrefix}${functionId}`;
|
|
115
|
+
}
|
|
116
|
+
if (functionName === undefined || functionId === undefined) {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
if (anonymous) {
|
|
120
|
+
info.graph.addEdge(arg, functionId, edge_1.EdgeType.Calls | edge_1.EdgeType.Reads);
|
|
121
|
+
const dfVert = info.graph.getVertex(call);
|
|
122
|
+
if (dfVert && dfVert.tag === vertex_1.VertexType.FunctionDefinition) {
|
|
123
|
+
// resolve all ingoings against the environment
|
|
124
|
+
const ingoingRefs = dfVert.subflow.in;
|
|
125
|
+
const remainingIn = [];
|
|
126
|
+
for (const ingoing of ingoingRefs) {
|
|
127
|
+
const resolved = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, data.environment, ingoing.type) : undefined;
|
|
128
|
+
if (resolved === undefined) {
|
|
129
|
+
remainingIn.push(ingoing);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${ingoing.nodeId} in closure of function definition ${call}`);
|
|
133
|
+
let allBuiltIn = true;
|
|
134
|
+
for (const ref of resolved) {
|
|
135
|
+
info.graph.addEdge(ingoing, ref, edge_1.EdgeType.Reads);
|
|
136
|
+
info.graph.addEdge(call, ref, edge_1.EdgeType.Reads); // because the def. is the anonymous call
|
|
137
|
+
if (!(0, identifier_1.isReferenceType)(ref.type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) {
|
|
138
|
+
allBuiltIn = false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (allBuiltIn) {
|
|
142
|
+
remainingIn.push(ingoing);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
dfVert.subflow.in = remainingIn;
|
|
146
|
+
}
|
|
147
|
+
// we did the linking
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
info.graph.updateToFunctionCall({
|
|
152
|
+
tag: vertex_1.VertexType.FunctionCall,
|
|
153
|
+
id: functionId,
|
|
154
|
+
name: functionName,
|
|
155
|
+
args: [],
|
|
156
|
+
environment: data.environment,
|
|
157
|
+
onlyBuiltin: false,
|
|
158
|
+
cds: data.cds,
|
|
159
|
+
origin: [built_in_1.BuiltInProcName.Function]
|
|
160
|
+
});
|
|
161
|
+
return functionId;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
87
164
|
function getExitPoints(vertex, graph) {
|
|
88
165
|
if (!vertex) {
|
|
89
166
|
return undefined;
|
|
@@ -291,7 +291,7 @@ ${(0, doc_code_1.codeBlock)('json', JSON.stringify({
|
|
|
291
291
|
| Type | Description | Example |
|
|
292
292
|
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
293
293
|
| \`constant\` | Additionally allows for a \`value\` this should resolve to. | \`{ type: 'constant', names: ['NULL', 'NA'], value: null }\` |
|
|
294
|
-
| \`function\` | Is a rather flexible way to define and bind built-in functions. For the time, we do not have extensive documentation to cover all the cases, so please either consult the sources with the \`default-builtin-config.ts\` or open a [new issue](${doc_issue_1.NewIssueUrl}). | \`{ type: 'function', names: ['next'], processor: '
|
|
294
|
+
| \`function\` | Is a rather flexible way to define and bind built-in functions. For the time, we do not have extensive documentation to cover all the cases, so please either consult the sources with the \`default-builtin-config.ts\` or open a [new issue](${doc_issue_1.NewIssueUrl}). | \`{ type: 'function', names: ['next'], processor: '${built_in_1.BuiltInProcName.Default}', config: { cfg: ExitPointType.Next } }\` |
|
|
295
295
|
| \`replacement\` | A comfortable way to specify replacement functions like \`$<-\` or \`names<-\`. \`suffixes\` describes the... suffixes to attach automatically. | \`{ type: 'replacement', suffixes: ['<-', '<<-'], names: ['[', '[['] }\` |
|
|
296
296
|
|
|
297
297
|
|
|
@@ -102,9 +102,9 @@ export interface LintingResult {
|
|
|
102
102
|
*/
|
|
103
103
|
readonly quickFix?: LintQuickFix[];
|
|
104
104
|
/**
|
|
105
|
-
* The node ID involved in this linting result, if applicable.
|
|
105
|
+
* The node ID or IDs involved in this linting result, if applicable.
|
|
106
106
|
*/
|
|
107
|
-
readonly involvedId: NodeId | undefined;
|
|
107
|
+
readonly involvedId: NodeId | NodeId[] | undefined;
|
|
108
108
|
}
|
|
109
109
|
export interface ConfiguredLintingRule<Name extends LintingRuleNames = LintingRuleNames> {
|
|
110
110
|
readonly name: Name;
|
package/linter/linter-rules.d.ts
CHANGED
|
@@ -249,11 +249,7 @@ export declare const LintingRules: {
|
|
|
249
249
|
cfg: import("../control-flow/control-flow-graph").ControlFlowInformation;
|
|
250
250
|
analyzer: import("../project/flowr-analyzer").ReadonlyFlowrAnalysisProvider;
|
|
251
251
|
}) => {
|
|
252
|
-
results:
|
|
253
|
-
certainty: import("./linter-format").LintingResultCertainty.Certain;
|
|
254
|
-
involvedId: undefined;
|
|
255
|
-
range: import("../util/range").SourceRange;
|
|
256
|
-
}[];
|
|
252
|
+
results: import("./rules/dead-code").DeadCodeResult[];
|
|
257
253
|
'.meta': import("./rules/dead-code").DeadCodeMetadata;
|
|
258
254
|
};
|
|
259
255
|
readonly prettyPrint: {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type LintingResult, LintingRuleCertainty } from '../linter-format';
|
|
2
2
|
import { type SourceRange } from '../../util/range';
|
|
3
3
|
import type { MergeableRecord } from '../../util/objects';
|
|
4
4
|
import { LintingRuleTag } from '../linter-tags';
|
|
5
5
|
import { type CfgSimplificationPassName } from '../../control-flow/cfg-simplification';
|
|
6
6
|
export interface DeadCodeResult extends LintingResult {
|
|
7
|
-
range: SourceRange;
|
|
7
|
+
readonly range: SourceRange;
|
|
8
8
|
}
|
|
9
9
|
export interface DeadCodeConfig extends MergeableRecord {
|
|
10
10
|
/**
|
|
@@ -24,11 +24,7 @@ export declare const DEAD_CODE: {
|
|
|
24
24
|
cfg: import("../../control-flow/control-flow-graph").ControlFlowInformation;
|
|
25
25
|
analyzer: import("../../project/flowr-analyzer").ReadonlyFlowrAnalysisProvider;
|
|
26
26
|
}) => {
|
|
27
|
-
results:
|
|
28
|
-
certainty: LintingResultCertainty.Certain;
|
|
29
|
-
involvedId: undefined;
|
|
30
|
-
range: SourceRange;
|
|
31
|
-
}[];
|
|
27
|
+
results: DeadCodeResult[];
|
|
32
28
|
'.meta': DeadCodeMetadata;
|
|
33
29
|
};
|
|
34
30
|
readonly prettyPrint: {
|
|
@@ -19,19 +19,18 @@ exports.DEAD_CODE = {
|
|
|
19
19
|
consideredNodes: 0
|
|
20
20
|
};
|
|
21
21
|
return {
|
|
22
|
-
results: (
|
|
22
|
+
results: combineResults(elements.getElements()
|
|
23
23
|
.filter(element => {
|
|
24
24
|
meta.consideredNodes++;
|
|
25
25
|
const cfgInformation = (0, search_enrichers_1.enrichmentContent)(element, search_enrichers_1.Enrichment.CfgInformation);
|
|
26
26
|
return cfgInformation.isRoot && !cfgInformation.isReachable;
|
|
27
27
|
})
|
|
28
|
-
.map(element =>
|
|
29
|
-
.filter(assert_1.isNotUndefined)))
|
|
30
|
-
.map(range => ({
|
|
28
|
+
.map(element => ({
|
|
31
29
|
certainty: linter_format_1.LintingResultCertainty.Certain,
|
|
32
|
-
involvedId:
|
|
33
|
-
range
|
|
34
|
-
}))
|
|
30
|
+
involvedId: element.node.info.id,
|
|
31
|
+
range: (element.node.info.fullRange ?? element.node.location)
|
|
32
|
+
}))
|
|
33
|
+
.filter(element => (0, assert_1.isNotUndefined)(element.range))),
|
|
35
34
|
'.meta': meta
|
|
36
35
|
};
|
|
37
36
|
},
|
|
@@ -48,4 +47,23 @@ exports.DEAD_CODE = {
|
|
|
48
47
|
defaultConfig: {}
|
|
49
48
|
}
|
|
50
49
|
};
|
|
50
|
+
function combineResults(results) {
|
|
51
|
+
for (let i = results.length - 1; i >= 0; i--) {
|
|
52
|
+
const result = results[i];
|
|
53
|
+
const other = results.find(other => result !== other && (0, range_1.rangeIsSubsetOf)(result.range, other.range));
|
|
54
|
+
if (other !== undefined) {
|
|
55
|
+
if (!Array.isArray(other.involvedId)) {
|
|
56
|
+
other.involvedId = other.involvedId !== undefined ? [other.involvedId] : [];
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(result.involvedId)) {
|
|
59
|
+
other.involvedId.push(...result.involvedId);
|
|
60
|
+
}
|
|
61
|
+
else if (result.involvedId !== undefined) {
|
|
62
|
+
other.involvedId.push(result.involvedId);
|
|
63
|
+
}
|
|
64
|
+
results.splice(i, 1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return results;
|
|
68
|
+
}
|
|
51
69
|
//# sourceMappingURL=dead-code.js.map
|
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FlowrNamespaceFile = void 0;
|
|
4
4
|
const flowr_file_1 = require("../../../context/flowr-file");
|
|
5
5
|
const resolve_args_1 = require("../../../../abstract-interpretation/data-frame/resolve-args");
|
|
6
|
+
const retriever_1 = require("../../../../r-bridge/retriever");
|
|
6
7
|
/**
|
|
7
8
|
* This decorates a text file and provides access to its content in the {@link NamespaceFormat}.
|
|
8
9
|
*/
|
|
@@ -60,11 +61,11 @@ function parseNamespace(file) {
|
|
|
60
61
|
switch (type) {
|
|
61
62
|
case 'exportClasses':
|
|
62
63
|
case 'exportMethods':
|
|
63
|
-
result.current.exportedFunctions.push(args);
|
|
64
|
+
result.current.exportedFunctions.push((0, retriever_1.removeRQuotes)(args));
|
|
64
65
|
break;
|
|
65
66
|
case 'S3method':
|
|
66
67
|
{
|
|
67
|
-
const parts = args.split(',').map(s => s.trim());
|
|
68
|
+
const parts = args.split(',').map(s => (0, retriever_1.removeRQuotes)(s.trim()));
|
|
68
69
|
if (parts.length !== 2) {
|
|
69
70
|
continue;
|
|
70
71
|
}
|
|
@@ -78,7 +79,7 @@ function parseNamespace(file) {
|
|
|
78
79
|
break;
|
|
79
80
|
}
|
|
80
81
|
case 'export':
|
|
81
|
-
result.current.exportedSymbols.push(args);
|
|
82
|
+
result.current.exportedSymbols.push((0, retriever_1.removeRQuotes)(args));
|
|
82
83
|
break;
|
|
83
84
|
case 'useDynLib':
|
|
84
85
|
{
|
|
@@ -39,7 +39,10 @@ async function executeDependenciesQuery({ analyzer, }, queries) {
|
|
|
39
39
|
const results = Object.fromEntries(await Promise.all(functions.entries().map(async ([c, f]) => {
|
|
40
40
|
const results = getResults(queries, { dataflow, config, normalize }, queryResults, c, f, data);
|
|
41
41
|
// only default categories allow additional analyses, so we null-coalesce here!
|
|
42
|
-
|
|
42
|
+
const enabled = query.enabledCategories;
|
|
43
|
+
if (enabled === undefined || (enabled?.length > 0 && enabled.includes(c))) {
|
|
44
|
+
await dependencies_query_format_1.DefaultDependencyCategories[c]?.additionalAnalysis?.(data, ignoreDefault, f, queryResults, results);
|
|
45
|
+
}
|
|
43
46
|
return [c, results];
|
|
44
47
|
})));
|
|
45
48
|
return {
|
|
@@ -202,7 +205,7 @@ function collectValuesFromLinks(args, data, linkedIds) {
|
|
|
202
205
|
}
|
|
203
206
|
function getFunctionsToCheck(customFunctions, functionFlag, enabled, ignoreDefaultFunctions, defaultFunctions) {
|
|
204
207
|
// "If unset or empty, all function types are searched for."
|
|
205
|
-
if (enabled?.length
|
|
208
|
+
if (enabled !== undefined && (enabled?.length === 0 || enabled.indexOf(functionFlag) < 0)) {
|
|
206
209
|
return [];
|
|
207
210
|
}
|
|
208
211
|
let functions = ignoreDefaultFunctions ? [] : [...defaultFunctions];
|
|
@@ -119,7 +119,7 @@ exports.DependenciesQueryDefinition = {
|
|
|
119
119
|
type: joi_1.default.string().valid('dependencies').required().description('The type of the query.'),
|
|
120
120
|
ignoreDefaultFunctions: joi_1.default.boolean().optional().description('Should the set of functions that are detected by default be ignored/skipped? Defaults to false.'),
|
|
121
121
|
...Object.fromEntries(Object.keys(exports.DefaultDependencyCategories).map(c => [`${c}Functions`, functionInfoSchema.description(`The set of ${c} functions to search for.`)])),
|
|
122
|
-
enabledCategories: joi_1.default.array().optional().items(joi_1.default.string()
|
|
122
|
+
enabledCategories: joi_1.default.array().optional().items(joi_1.default.string()).description('A set of flags that determines what types of dependencies are searched for. If unset, all dependency types are searched for.'),
|
|
123
123
|
additionalCategories: joi_1.default.object().allow(joi_1.default.object({
|
|
124
124
|
queryDisplayName: joi_1.default.string().description('The display name in the query result.'),
|
|
125
125
|
functions: functionInfoSchema.description('The functions that this additional category should search for.'),
|
|
@@ -111,7 +111,7 @@ exports.LinterQueryDefinition = {
|
|
|
111
111
|
if ((0, linter_format_1.isLintingResultsError)(v)) {
|
|
112
112
|
return [];
|
|
113
113
|
}
|
|
114
|
-
return v.results.
|
|
114
|
+
return v.results.flatMap(v => Array.isArray(v.involvedId) ? v.involvedId : [v.involvedId]);
|
|
115
115
|
}).filter(assert_1.isNotUndefined);
|
|
116
116
|
}
|
|
117
117
|
};
|
|
@@ -12,6 +12,7 @@ import type { SourceRange } from '../../../../../util/range';
|
|
|
12
12
|
import { BiMap } from '../../../../../util/collections/bimap';
|
|
13
13
|
import type { MergeableRecord } from '../../../../../util/objects';
|
|
14
14
|
import { RoleInParent } from './role';
|
|
15
|
+
import { RType } from '../type';
|
|
15
16
|
import type { NodeId } from './node-id';
|
|
16
17
|
import type { RDelimiter } from '../nodes/info/r-delimiter';
|
|
17
18
|
import type { RProject } from '../nodes/r-project';
|
|
@@ -49,7 +50,7 @@ export interface ParentContextInfo extends MergeableRecord {
|
|
|
49
50
|
/**
|
|
50
51
|
* The nesting of the node in the AST
|
|
51
52
|
*
|
|
52
|
-
* The root node has a nesting of 0,
|
|
53
|
+
* The root node has a nesting of 0, contexts listed in ${@link nestForElement} will increase the nesting
|
|
53
54
|
*/
|
|
54
55
|
nesting: number;
|
|
55
56
|
/**
|
|
@@ -80,6 +81,10 @@ export interface NormalizedAst<OtherInfo = ParentInformation, Node = RProject<Ot
|
|
|
80
81
|
/** marks whether the AST contains potential syntax errors */
|
|
81
82
|
hasError?: boolean;
|
|
82
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Node types for which the nesting score will be increased
|
|
86
|
+
*/
|
|
87
|
+
export declare const nestForElement: ReadonlySet<RType>;
|
|
83
88
|
export interface NormalizedAstDecorationConfiguration<OtherInfo> {
|
|
84
89
|
/** The id generator: must generate a unique id für each passed node */
|
|
85
90
|
getId?: IdGenerator<OtherInfo>;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.nestForElement = void 0;
|
|
12
13
|
exports.deterministicCountingIdGenerator = deterministicCountingIdGenerator;
|
|
13
14
|
exports.deterministicPrefixIdGenerator = deterministicPrefixIdGenerator;
|
|
14
15
|
exports.sourcedDeterministicCountingIdGenerator = sourcedDeterministicCountingIdGenerator;
|
|
@@ -64,7 +65,10 @@ const defaultParentContext = {
|
|
|
64
65
|
role: "root" /* RoleInParent.Root */,
|
|
65
66
|
index: 0
|
|
66
67
|
};
|
|
67
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Node types for which the nesting score will be increased
|
|
70
|
+
*/
|
|
71
|
+
exports.nestForElement = new Set([
|
|
68
72
|
type_1.RType.FunctionDefinition, type_1.RType.ForLoop, type_1.RType.WhileLoop, type_1.RType.RepeatLoop, type_1.RType.IfThenElse,
|
|
69
73
|
]);
|
|
70
74
|
/**
|
|
@@ -83,7 +87,7 @@ function decorateAst(project, { getId = deterministicCountingIdGenerator(0) }) {
|
|
|
83
87
|
const unaryOp = createFoldForUnaryOp(info);
|
|
84
88
|
const folds = {
|
|
85
89
|
down: (n, nesting) => {
|
|
86
|
-
if (nestForElement.has(n.type)) {
|
|
90
|
+
if (exports.nestForElement.has(n.type)) {
|
|
87
91
|
return nesting + 1;
|
|
88
92
|
}
|
|
89
93
|
else {
|
package/util/mermaid/cfg.d.ts
CHANGED
|
@@ -2,9 +2,14 @@ import type { NormalizedAst } from '../../r-bridge/lang-4.x/ast/model/processing
|
|
|
2
2
|
import { type ControlFlowInformation } from '../../control-flow/control-flow-graph';
|
|
3
3
|
import type { MermaidMarkStyle, MermaidGraphPrinterInfo } from './info';
|
|
4
4
|
export interface MermaidCfgGraphPrinterInfo extends MermaidGraphPrinterInfo {
|
|
5
|
+
/** The style to apply to mark an entry point marker node */
|
|
5
6
|
entryPointStyle?: MermaidMarkStyle['vertex'];
|
|
7
|
+
/** The stly eto apply to mark an exit point marker node */
|
|
6
8
|
exitPointStyle?: MermaidMarkStyle['vertex'];
|
|
9
|
+
/** If true, a simplified basic block will have "Basic Block (id)" prepended */
|
|
7
10
|
includeBasicBlockLabel?: boolean;
|
|
11
|
+
/** If this threshold is reached (lexemes of a simplified basic block), the remaning character will be replaced by ... */
|
|
12
|
+
basicBlockCharacterLimit?: number;
|
|
8
13
|
}
|
|
9
14
|
export declare const MermaidEntryPointDefaultMarkStyle: MermaidMarkStyle['vertex'];
|
|
10
15
|
export declare const MermaidExitPointDefaultMarkStyle: MermaidMarkStyle['vertex'];
|
|
@@ -18,7 +23,7 @@ export declare const MermaidExitPointDefaultMarkStyle: MermaidMarkStyle['vertex'
|
|
|
18
23
|
* @param includeOnlyIds - If provided, only include the vertices with the given IDs.
|
|
19
24
|
* @param mark - If provided, mark the given vertices and edges.
|
|
20
25
|
*/
|
|
21
|
-
export declare function cfgToMermaid(cfg: ControlFlowInformation, normalizedAst: NormalizedAst, { prefix, simplify, markStyle, entryPointStyle, exitPointStyle, includeOnlyIds, mark, includeBasicBlockLabel }?: MermaidCfgGraphPrinterInfo): string;
|
|
26
|
+
export declare function cfgToMermaid(cfg: ControlFlowInformation, normalizedAst: NormalizedAst, { prefix, simplify, markStyle, entryPointStyle, exitPointStyle, includeOnlyIds, mark, includeBasicBlockLabel, basicBlockCharacterLimit }?: MermaidCfgGraphPrinterInfo): string;
|
|
22
27
|
/**
|
|
23
28
|
* Use mermaid to visualize the normalized AST.
|
|
24
29
|
*/
|
package/util/mermaid/cfg.js
CHANGED
|
@@ -50,7 +50,7 @@ function shouldIncludeNode(simplify, element, include) {
|
|
|
50
50
|
* @param includeOnlyIds - If provided, only include the vertices with the given IDs.
|
|
51
51
|
* @param mark - If provided, mark the given vertices and edges.
|
|
52
52
|
*/
|
|
53
|
-
function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify = false, markStyle = info_1.MermaidDefaultMarkStyle, entryPointStyle = exports.MermaidEntryPointDefaultMarkStyle, exitPointStyle = exports.MermaidExitPointDefaultMarkStyle, includeOnlyIds, mark, includeBasicBlockLabel = true } = {}) {
|
|
53
|
+
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
54
|
const hasBbandSimplify = simplify && cfg.graph.mayHaveBasicBlocks();
|
|
55
55
|
let output = prefix;
|
|
56
56
|
if (includeOnlyIds) {
|
|
@@ -89,7 +89,7 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
|
|
|
89
89
|
continue;
|
|
90
90
|
}
|
|
91
91
|
const ids = vertex.elems?.map(e => e.id) ?? [];
|
|
92
|
-
const reconstruct = (0, reconstruct_1.reconstructToCode)(normalizedAst, { nodes: new Set(ids) }, auto_select_defaults_1.doNotAutoSelect).code;
|
|
92
|
+
const reconstruct = limitTo((0, reconstruct_1.reconstructToCode)(normalizedAst, { nodes: new Set(ids) }, auto_select_defaults_1.doNotAutoSelect).code, basicBlockCharacterLimit);
|
|
93
93
|
const name = `"\`${includeBasicBlockLabel ? `Basic Block (${id})\n` : ''}${(0, mermaid_1.escapeMarkdown)(reconstruct)}\`"`;
|
|
94
94
|
output += ` n${id}[[${name}]]\n`;
|
|
95
95
|
diagramIncludedIds.add(vertex.id);
|
|
@@ -166,4 +166,13 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
|
|
|
166
166
|
function cfgToMermaidUrl(cfg, normalizedAst, info) {
|
|
167
167
|
return (0, mermaid_1.mermaidCodeToUrl)(cfgToMermaid(cfg, normalizedAst, info ?? {}));
|
|
168
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Limits a string to n chars, after which the remainder will be replaced with ...
|
|
171
|
+
*/
|
|
172
|
+
function limitTo(str, limit) {
|
|
173
|
+
if (str.length <= limit) {
|
|
174
|
+
return str;
|
|
175
|
+
}
|
|
176
|
+
return `${str.substring(0, limit)}...`;
|
|
177
|
+
}
|
|
169
178
|
//# sourceMappingURL=cfg.js.map
|
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.8.
|
|
9
|
+
const version = '2.8.10';
|
|
10
10
|
/**
|
|
11
11
|
* Retrieves the current flowR version as a new {@link SemVer} object.
|
|
12
12
|
*/
|