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