@eagleoutice/flowr 2.7.0 → 2.7.3
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 +14 -14
- package/abstract-interpretation/absint-visitor.d.ts +160 -0
- package/abstract-interpretation/absint-visitor.js +279 -0
- package/abstract-interpretation/data-frame/dataframe-domain.d.ts +2 -2
- package/abstract-interpretation/data-frame/dataframe-domain.js +23 -7
- package/abstract-interpretation/data-frame/mappers/access-mapper.d.ts +6 -6
- package/abstract-interpretation/data-frame/mappers/access-mapper.js +10 -14
- package/abstract-interpretation/data-frame/mappers/arguments.d.ts +15 -9
- package/abstract-interpretation/data-frame/mappers/arguments.js +27 -4
- package/abstract-interpretation/data-frame/mappers/function-mapper.d.ts +17 -17
- package/abstract-interpretation/data-frame/mappers/function-mapper.js +55 -67
- package/abstract-interpretation/data-frame/mappers/replacement-mapper.d.ts +7 -7
- package/abstract-interpretation/data-frame/mappers/replacement-mapper.js +25 -29
- package/abstract-interpretation/data-frame/resolve-args.d.ts +1 -1
- package/abstract-interpretation/data-frame/resolve-args.js +1 -1
- package/abstract-interpretation/data-frame/semantics.js +5 -6
- package/abstract-interpretation/data-frame/shape-inference.d.ts +52 -28
- package/abstract-interpretation/data-frame/shape-inference.js +67 -90
- package/abstract-interpretation/domains/bounded-set-domain.d.ts +2 -2
- package/abstract-interpretation/domains/interval-domain.d.ts +2 -2
- package/abstract-interpretation/domains/set-range-domain.d.ts +10 -4
- package/abstract-interpretation/domains/set-range-domain.js +7 -1
- package/abstract-interpretation/domains/set-upper-bound-domain.d.ts +2 -2
- package/abstract-interpretation/domains/singleton-domain.d.ts +2 -2
- package/benchmark/slicer.js +13 -14
- package/cli/common/options.d.ts +431 -8
- package/cli/common/options.js +1 -1
- package/cli/common/scripts-info.d.ts +431 -7
- package/cli/flowr-main-options.d.ts +102 -2
- package/cli/flowr.d.ts +102 -2
- package/cli/repl/commands/repl-commands.d.ts +25 -0
- package/cli/repl/commands/repl-query.js +17 -5
- package/cli/wiki.d.ts +13 -0
- package/cli/wiki.js +7 -2
- package/config.d.ts +4 -4
- package/config.js +1 -1
- package/control-flow/basic-cfg-guided-visitor.js +7 -8
- package/control-flow/control-flow-graph.d.ts +1 -1
- package/control-flow/semantic-cfg-guided-visitor.d.ts +1 -1
- package/control-flow/semantic-cfg-guided-visitor.js +1 -1
- package/dataflow/eval/resolve/alias-tracking.js +1 -1
- package/dataflow/internal/linker.d.ts +2 -0
- package/dataflow/internal/linker.js +10 -12
- package/documentation/doc-capabilities.d.ts +1 -1
- package/documentation/doc-readme.d.ts +1 -1
- package/documentation/doc-util/doc-cfg.js +1 -1
- package/documentation/doc-util/doc-cli-option.d.ts +6 -6
- package/documentation/doc-util/doc-cli-option.js +3 -3
- package/documentation/doc-util/doc-dfg.d.ts +1 -1
- package/documentation/doc-util/doc-files.d.ts +3 -0
- package/documentation/doc-util/doc-files.js +4 -1
- package/documentation/doc-util/doc-normalized-ast.js +2 -2
- package/documentation/issue-linting-rule.d.ts +1 -1
- package/documentation/wiki-analyzer.d.ts +1 -1
- package/documentation/wiki-cfg.d.ts +1 -1
- package/documentation/wiki-core.d.ts +1 -1
- package/documentation/wiki-dataflow-graph.d.ts +1 -1
- package/documentation/wiki-dataflow-graph.js +6 -6
- package/documentation/wiki-engine.d.ts +1 -1
- package/documentation/wiki-engine.js +9 -10
- package/documentation/wiki-faq.d.ts +1 -1
- package/documentation/wiki-interface.d.ts +1 -1
- package/documentation/wiki-interface.js +12 -13
- package/documentation/wiki-linter.d.ts +1 -1
- package/documentation/wiki-linting-and-testing.d.ts +1 -1
- package/documentation/wiki-mk/doc-context.d.ts +54 -1
- package/documentation/wiki-mk/doc-context.js +17 -0
- package/documentation/wiki-mk/doc-maker.d.ts +5 -5
- package/documentation/wiki-mk/doc-maker.js +3 -1
- package/documentation/wiki-normalized-ast.d.ts +1 -1
- package/documentation/wiki-onboarding.d.ts +1 -1
- package/documentation/wiki-overview.d.ts +9 -0
- package/documentation/wiki-overview.js +248 -0
- package/documentation/wiki-query.d.ts +1 -1
- package/documentation/wiki-query.js +17 -1
- package/documentation/wiki-search.d.ts +1 -1
- package/documentation/wiki-setup.d.ts +9 -0
- package/documentation/wiki-setup.js +122 -0
- package/linter/rules/dataframe-access-validation.d.ts +1 -1
- package/linter/rules/dataframe-access-validation.js +8 -10
- package/linter/rules/unused-definition.js +1 -1
- package/package.json +1 -1
- package/project/context/flowr-analyzer-context.d.ts +4 -0
- package/project/context/flowr-analyzer-context.js +3 -1
- package/project/context/flowr-analyzer-dependencies-context.d.ts +3 -2
- package/project/context/flowr-analyzer-dependencies-context.js +4 -2
- package/project/context/flowr-analyzer-files-context.d.ts +9 -1
- package/project/context/flowr-analyzer-files-context.js +4 -0
- package/project/context/flowr-analyzer-functions-context.d.ts +29 -0
- package/project/context/flowr-analyzer-functions-context.js +68 -0
- package/project/context/flowr-file.d.ts +2 -0
- package/project/context/flowr-file.js +2 -0
- package/project/plugins/file-plugins/{flowr-description-file.d.ts → files/flowr-description-file.d.ts} +1 -1
- package/project/plugins/file-plugins/files/flowr-description-file.js +75 -0
- package/project/plugins/file-plugins/files/flowr-namespace-file.d.ts +32 -0
- package/project/plugins/file-plugins/files/flowr-namespace-file.js +102 -0
- package/project/plugins/file-plugins/files/flowr-news-file.d.ts +27 -0
- package/project/plugins/file-plugins/files/flowr-news-file.js +152 -0
- package/project/plugins/file-plugins/flowr-analyzer-description-file-plugin.d.ts +1 -1
- package/project/plugins/file-plugins/flowr-analyzer-description-file-plugin.js +1 -1
- package/project/plugins/file-plugins/flowr-analyzer-namespace-file-plugin.d.ts +22 -0
- package/project/plugins/file-plugins/flowr-analyzer-namespace-file-plugin.js +34 -0
- package/project/plugins/file-plugins/flowr-analyzer-news-file-plugin.d.ts +23 -0
- package/project/plugins/file-plugins/flowr-analyzer-news-file-plugin.js +35 -0
- package/project/plugins/file-plugins/notebooks/flowr-analyzer-jupyter-file-plugin.d.ts +1 -1
- package/project/plugins/file-plugins/notebooks/flowr-analyzer-jupyter-file-plugin.js +1 -1
- package/project/plugins/file-plugins/notebooks/flowr-analyzer-qmd-file-plugin.d.ts +1 -1
- package/project/plugins/file-plugins/notebooks/flowr-analyzer-qmd-file-plugin.js +1 -1
- package/project/plugins/file-plugins/notebooks/flowr-analyzer-rmd-file-plugin.d.ts +1 -1
- package/project/plugins/file-plugins/notebooks/flowr-analyzer-rmd-file-plugin.js +1 -1
- package/project/plugins/flowr-analyzer-plugin-defaults.js +4 -0
- package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-description-file-plugin.js +5 -1
- package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-namespace-file-plugin.d.ts +10 -0
- package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-namespace-file-plugin.js +56 -0
- package/project/plugins/package-version-plugins/package.d.ts +15 -2
- package/project/plugins/package-version-plugins/package.js +33 -5
- package/project/plugins/plugin-registry.d.ts +3 -1
- package/project/plugins/plugin-registry.js +4 -0
- package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +7 -1
- package/queries/catalog/dependencies-query/dependencies-query-executor.js +2 -1
- package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +2 -0
- package/queries/catalog/dependencies-query/dependencies-query-format.js +2 -1
- package/queries/catalog/df-shape-query/df-shape-query-executor.js +4 -2
- package/queries/catalog/files-query/files-query-executor.d.ts +6 -0
- package/queries/catalog/files-query/files-query-executor.js +49 -0
- package/queries/catalog/files-query/files-query-format.d.ts +36 -0
- package/queries/catalog/files-query/files-query-format.js +114 -0
- package/queries/catalog/linter-query/linter-query-format.js +1 -1
- package/queries/query.d.ts +10 -1
- package/queries/query.js +3 -1
- package/r-bridge/lang-4.x/ast/model/model.d.ts +1 -1
- package/r-bridge/lang-4.x/ast/model/processing/decorate.js +8 -8
- package/r-bridge/lang-4.x/ast/model/processing/role.d.ts +8 -8
- package/r-bridge/lang-4.x/ast/parser/main/internal/functions/normalize-parameter.js +0 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +0 -1
- package/statistics/features/supported/data-access/data-access.js +1 -1
- package/util/containers.js +1 -1
- package/util/files.d.ts +0 -7
- package/util/files.js +0 -41
- package/util/mermaid/ast.d.ts +3 -2
- package/util/mermaid/ast.js +13 -7
- package/util/mermaid/cfg.d.ts +3 -2
- package/util/mermaid/cfg.js +26 -6
- package/util/mermaid/dfg.d.ts +1 -7
- package/util/mermaid/dfg.js +7 -3
- package/util/mermaid/info.d.ts +17 -0
- package/util/mermaid/info.js +5 -0
- package/util/prefix.d.ts +9 -5
- package/util/prefix.js +14 -6
- package/util/r-regex.d.ts +21 -0
- package/util/r-regex.js +25 -0
- package/util/text/args.js +12 -3
- package/util/version.js +1 -1
- package/abstract-interpretation/data-frame/absint-info.d.ts +0 -109
- package/abstract-interpretation/data-frame/absint-info.js +0 -31
- package/abstract-interpretation/data-frame/absint-visitor.d.ts +0 -57
- package/abstract-interpretation/data-frame/absint-visitor.js +0 -176
- package/abstract-interpretation/data-frame/mappers/assignment-mapper.d.ts +0 -21
- package/abstract-interpretation/data-frame/mappers/assignment-mapper.js +0 -34
- package/documentation/doc-util/doc-print.d.ts +0 -5
- package/documentation/doc-util/doc-print.js +0 -36
- package/project/plugins/file-plugins/flowr-description-file.js +0 -37
- package/project/plugins/file-plugins/notebooks/notebook.d.ts +0 -0
- package/project/plugins/file-plugins/notebooks/notebook.js +0 -2
- /package/project/plugins/file-plugins/{notebooks → files}/flowr-jupyter-file.d.ts +0 -0
- /package/project/plugins/file-plugins/{notebooks → files}/flowr-jupyter-file.js +0 -0
- /package/project/plugins/file-plugins/{notebooks → files}/flowr-rmarkdown-file.d.ts +0 -0
- /package/project/plugins/file-plugins/{notebooks → files}/flowr-rmarkdown-file.js +0 -0
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.
|
|
27
|
+
flowR repl using flowR v2.7.2, R grammar v14 (tree-sitter engine)
|
|
28
28
|
R> :query @linter "read.csv(\"/root/x.txt\")"
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -33,9 +33,9 @@ It offers a wide variety of features, for example:
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
```text
|
|
36
|
-
Query: [;1mlinter[0m (
|
|
36
|
+
Query: [;1mlinter[0m (1 ms)
|
|
37
37
|
╰ **Deprecated Functions** (deprecated-functions):
|
|
38
|
-
╰ _Metadata_: <code>{"totalCalls":0,"totalFunctionDefinitions":0,"searchTimeMs":
|
|
38
|
+
╰ _Metadata_: <code>{"totalCalls":0,"totalFunctionDefinitions":0,"searchTimeMs":0,"processTimeMs":0}</code>
|
|
39
39
|
╰ **File Path Validity** (file-path-validity):
|
|
40
40
|
╰ certain:
|
|
41
41
|
╰ Path `/root/x.txt` at 1.1-23
|
|
@@ -58,7 +58,7 @@ It offers a wide variety of features, for example:
|
|
|
58
58
|
╰ _Metadata_: <code>{"consideredNodes":5,"searchTimeMs":0,"processTimeMs":0}</code>
|
|
59
59
|
╰ **Useless Loops** (useless-loop):
|
|
60
60
|
╰ _Metadata_: <code>{"numOfUselessLoops":0,"searchTimeMs":0,"processTimeMs":0}</code>
|
|
61
|
-
[;3mAll queries together required ≈
|
|
61
|
+
[;3mAll queries together required ≈1 ms (1ms accuracy, total 2 ms)[0m[0m
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
|
|
@@ -92,7 +92,7 @@ It offers a wide variety of features, for example:
|
|
|
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":0,"processTimeMs":1}</code>\
|
|
96
96
|
╰ **Unused Definitions** (unused-definitions):\
|
|
97
97
|
╰ _Metadata_: <code>{"totalConsidered":0,"searchTimeMs":0,"processTimeMs":0}</code>\
|
|
98
98
|
╰ **Naming Convention** (naming-convention):\
|
|
@@ -105,11 +105,11 @@ It offers a wide variety of features, for example:
|
|
|
105
105
|
╰ _Metadata_: <code>{"consideredNodes":5,"searchTimeMs":0,"processTimeMs":0}</code>\
|
|
106
106
|
╰ **Useless Loops** (useless-loop):\
|
|
107
107
|
╰ _Metadata_: <code>{"numOfUselessLoops":0,"searchTimeMs":0,"processTimeMs":0}</code>\
|
|
108
|
-
_All queries together required ≈3 ms (1ms accuracy, total
|
|
108
|
+
_All queries together required ≈3 ms (1ms accuracy, total 4 ms)_
|
|
109
109
|
|
|
110
110
|
<details> <summary style="color:gray">Show Detailed Results as Json</summary>
|
|
111
111
|
|
|
112
|
-
The analysis required _3.
|
|
112
|
+
The analysis required _3.5 ms_ (including parsing and normalization and the query) within the generation environment.
|
|
113
113
|
|
|
114
114
|
In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR.
|
|
115
115
|
Please consult the [Interface](https://github.com/flowr-analysis/flowr/wiki/Interface) wiki page for more information on how to get those.
|
|
@@ -180,8 +180,8 @@ It offers a wide variety of features, for example:
|
|
|
180
180
|
".meta": {
|
|
181
181
|
"totalConsidered": 1,
|
|
182
182
|
"totalUnknown": 0,
|
|
183
|
-
"searchTimeMs":
|
|
184
|
-
"processTimeMs":
|
|
183
|
+
"searchTimeMs": 0,
|
|
184
|
+
"processTimeMs": 1
|
|
185
185
|
}
|
|
186
186
|
},
|
|
187
187
|
"unused-definitions": {
|
|
@@ -307,7 +307,7 @@ It offers a wide variety of features, for example:
|
|
|
307
307
|
|
|
308
308
|
```shell
|
|
309
309
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
310
|
-
flowR repl using flowR v2.
|
|
310
|
+
flowR repl using flowR v2.7.2, R grammar v14 (tree-sitter engine)
|
|
311
311
|
R> :query @static-slice (11@sum) file://test/testfiles/example.R
|
|
312
312
|
```
|
|
313
313
|
|
|
@@ -321,7 +321,7 @@ It offers a wide variety of features, for example:
|
|
|
321
321
|
N <- 10
|
|
322
322
|
for(i in 1:(N-1)) sum <- sum + i + w
|
|
323
323
|
sum
|
|
324
|
-
[;3mAll queries together required ≈
|
|
324
|
+
[;3mAll queries together required ≈3 ms (1ms accuracy, total 3 ms)[0m[0m
|
|
325
325
|
```
|
|
326
326
|
|
|
327
327
|
|
|
@@ -355,7 +355,7 @@ It offers a wide variety of features, for example:
|
|
|
355
355
|
|
|
356
356
|
|
|
357
357
|
* 🚀 **fast data- and control-flow graphs**\
|
|
358
|
-
Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">
|
|
358
|
+
Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">123.4 ms</span></i> (as of Dec 17, 2025),
|
|
359
359
|
_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,
|
|
360
360
|
and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/Dataflow-Graph) for more details on the dataflow graph.
|
|
361
361
|
|
|
@@ -391,7 +391,7 @@ It offers a wide variety of features, for example:
|
|
|
391
391
|
|
|
392
392
|
```shell
|
|
393
393
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
394
|
-
flowR repl using flowR v2.
|
|
394
|
+
flowR repl using flowR v2.7.2, R grammar v14 (tree-sitter engine)
|
|
395
395
|
R> :dataflow* test/testfiles/example.R
|
|
396
396
|
```
|
|
397
397
|
|
|
@@ -696,7 +696,7 @@ It offers a wide variety of features, for example:
|
|
|
696
696
|
```
|
|
697
697
|
|
|
698
698
|
|
|
699
|
-
(The analysis required
|
|
699
|
+
(The analysis required _5.0 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
|
|
700
700
|
|
|
701
701
|
|
|
702
702
|
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { CfgSimpleVertex, ControlFlowInformation } from '../control-flow/control-flow-graph';
|
|
2
|
+
import type { SemanticCfgGuidedVisitorConfiguration } from '../control-flow/semantic-cfg-guided-visitor';
|
|
3
|
+
import { SemanticCfgGuidedVisitor } from '../control-flow/semantic-cfg-guided-visitor';
|
|
4
|
+
import type { DataflowGraph } from '../dataflow/graph/graph';
|
|
5
|
+
import { type DataflowGraphVertexFunctionCall, type DataflowGraphVertexVariableDefinition } from '../dataflow/graph/vertex';
|
|
6
|
+
import type { NoInfo, RNode } from '../r-bridge/lang-4.x/ast/model/model';
|
|
7
|
+
import type { NormalizedAst, ParentInformation } from '../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
8
|
+
import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
9
|
+
import { type AnyAbstractDomain } from './domains/abstract-domain';
|
|
10
|
+
import type { StateAbstractDomain } from './domains/state-abstract-domain';
|
|
11
|
+
export interface AbsintVisitorConfiguration<Domain extends AnyAbstractDomain, OtherInfo = NoInfo> extends Omit<SemanticCfgGuidedVisitorConfiguration<OtherInfo, ControlFlowInformation, NormalizedAst<OtherInfo>>, 'defaultVisitingOrder' | 'defaultVisitingType'> {
|
|
12
|
+
readonly domain: StateAbstractDomain<Domain>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A control flow graph visitor to perform abstract interpretation.
|
|
16
|
+
*
|
|
17
|
+
* However, the visitor does not yet support inter-procedural abstract interpretation and abstract condition semantics.
|
|
18
|
+
*/
|
|
19
|
+
export declare abstract class AbstractInterpretationVisitor<Domain extends AnyAbstractDomain, OtherInfo = NoInfo, Config extends AbsintVisitorConfiguration<Domain, OtherInfo> = AbsintVisitorConfiguration<Domain, OtherInfo>> extends SemanticCfgGuidedVisitor<OtherInfo, ControlFlowInformation, NormalizedAst<OtherInfo>, DataflowGraph, Config & {
|
|
20
|
+
defaultVisitingOrder: 'forward';
|
|
21
|
+
defaultVisitingType: 'exit';
|
|
22
|
+
}> {
|
|
23
|
+
/**
|
|
24
|
+
* The abstract trace of the abstract interpretation visitor mapping node IDs to the abstract state at the respective node.
|
|
25
|
+
*/
|
|
26
|
+
protected readonly trace: Map<NodeId, StateAbstractDomain<Domain>>;
|
|
27
|
+
/**
|
|
28
|
+
* A set of nodes representing variable definitions that have already been visited but whose assignment has not yet been processed.
|
|
29
|
+
*/
|
|
30
|
+
private readonly unassigned;
|
|
31
|
+
/**
|
|
32
|
+
* The current abstract state domain at the currently processed AST node.
|
|
33
|
+
*/
|
|
34
|
+
private currentState;
|
|
35
|
+
constructor(config: Config);
|
|
36
|
+
/**
|
|
37
|
+
* Resolves the inferred abstract value of an AST node.
|
|
38
|
+
* This requires that the abstract interpretation visitor has been completed, or at least started.
|
|
39
|
+
* @param id - The ID of the node to get the inferred value for
|
|
40
|
+
* @param state - An optional state abstract domain used to resolve the inferred abstract value (defaults to the state at the requested node)
|
|
41
|
+
* @returns The inferred abstract value of the node, or `undefined` if no value was inferred for the node
|
|
42
|
+
*/
|
|
43
|
+
getAbstractValue(id: RNode<ParentInformation & OtherInfo> | NodeId | undefined, state?: StateAbstractDomain<Domain>): Domain | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Gets the inferred abstract state at the location of a specific AST node.
|
|
46
|
+
* This requires that the abstract interpretation visitor has been completed, or at least started.
|
|
47
|
+
* @param id - The ID of the node to get the abstract state at
|
|
48
|
+
* @returns The abstract state at the node, or `undefined` if the node has no abstract state (i.e. the node has not been visited or is unreachable).
|
|
49
|
+
*/
|
|
50
|
+
getAbstractState(id: NodeId | undefined): StateAbstractDomain<Domain> | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Gets the inferred abstract state at the end of the program (exit nodes of the control flow graph).
|
|
53
|
+
* This requires that the abstract interpretation visitor has been completed, or at least started.
|
|
54
|
+
* @returns The inferred abstract state at the end of the program
|
|
55
|
+
*/
|
|
56
|
+
getEndState(): StateAbstractDomain<Domain>;
|
|
57
|
+
/**
|
|
58
|
+
* Gets the inferred abstract trace mapping AST nodes to the inferred abstract state at the respective node.
|
|
59
|
+
* @returns The inferred abstract trace of the program
|
|
60
|
+
*/
|
|
61
|
+
getAbstractTrace(): ReadonlyMap<NodeId, StateAbstractDomain<Domain>>;
|
|
62
|
+
start(): void;
|
|
63
|
+
protected visitNode(vertexId: NodeId): boolean;
|
|
64
|
+
protected onVariableDefinition({ vertex }: {
|
|
65
|
+
vertex: DataflowGraphVertexVariableDefinition;
|
|
66
|
+
}): void;
|
|
67
|
+
protected onAssignmentCall({ target, source }: {
|
|
68
|
+
call: DataflowGraphVertexFunctionCall;
|
|
69
|
+
target?: NodeId;
|
|
70
|
+
source?: NodeId;
|
|
71
|
+
}): void;
|
|
72
|
+
protected onReplacementCall({ call, target, source }: {
|
|
73
|
+
call: DataflowGraphVertexFunctionCall;
|
|
74
|
+
target?: NodeId;
|
|
75
|
+
source?: NodeId;
|
|
76
|
+
}): void;
|
|
77
|
+
protected onAccessCall({ call }: {
|
|
78
|
+
call: DataflowGraphVertexFunctionCall;
|
|
79
|
+
}): void;
|
|
80
|
+
protected onUnnamedCall({ call }: {
|
|
81
|
+
call: DataflowGraphVertexFunctionCall;
|
|
82
|
+
}): void;
|
|
83
|
+
protected onEvalFunctionCall({ call }: {
|
|
84
|
+
call: DataflowGraphVertexFunctionCall;
|
|
85
|
+
}): void;
|
|
86
|
+
protected onApplyFunctionCall({ call }: {
|
|
87
|
+
call: DataflowGraphVertexFunctionCall;
|
|
88
|
+
}): void;
|
|
89
|
+
protected onSourceCall({ call }: {
|
|
90
|
+
call: DataflowGraphVertexFunctionCall;
|
|
91
|
+
}): void;
|
|
92
|
+
protected onGetCall({ call }: {
|
|
93
|
+
call: DataflowGraphVertexFunctionCall;
|
|
94
|
+
}): void;
|
|
95
|
+
protected onRmCall({ call }: {
|
|
96
|
+
call: DataflowGraphVertexFunctionCall;
|
|
97
|
+
}): void;
|
|
98
|
+
protected onListCall({ call }: {
|
|
99
|
+
call: DataflowGraphVertexFunctionCall;
|
|
100
|
+
}): void;
|
|
101
|
+
protected onVectorCall({ call }: {
|
|
102
|
+
call: DataflowGraphVertexFunctionCall;
|
|
103
|
+
}): void;
|
|
104
|
+
protected onSpecialBinaryOpCall({ call }: {
|
|
105
|
+
call: DataflowGraphVertexFunctionCall;
|
|
106
|
+
}): void;
|
|
107
|
+
protected onQuoteCall({ call }: {
|
|
108
|
+
call: DataflowGraphVertexFunctionCall;
|
|
109
|
+
}): void;
|
|
110
|
+
protected onLibraryCall({ call }: {
|
|
111
|
+
call: DataflowGraphVertexFunctionCall;
|
|
112
|
+
}): void;
|
|
113
|
+
protected onDefaultFunctionCall({ call }: {
|
|
114
|
+
call: DataflowGraphVertexFunctionCall;
|
|
115
|
+
}): void;
|
|
116
|
+
/**
|
|
117
|
+
* Evaluates any function call visited by the abstract interpretation visitor by applying the abstract semantics of the function call to the current abstract state.
|
|
118
|
+
* @param call - The data flow vertex of the function call to evaluate
|
|
119
|
+
* @param state - The current abstract state before the evaluation of the function call
|
|
120
|
+
* @returns The abstract state after applying the abstract semantics of the function call
|
|
121
|
+
*/
|
|
122
|
+
protected abstract evalFunctionCall(call: DataflowGraphVertexFunctionCall, state: StateAbstractDomain<Domain>): StateAbstractDomain<Domain>;
|
|
123
|
+
/**
|
|
124
|
+
* Evaluates any replacement function call visited by the abstract interpretation visitor by applying the abstract semantics of the replacement call to the current abstract state (e.g. for `$<-`, `[<-`, `names<-`, ...).
|
|
125
|
+
* @param call - The data flow vertex of the replacement call to evaluate
|
|
126
|
+
* @param source - The node ID of the assignment target of the replacement call
|
|
127
|
+
* @param target - The node ID of the assigned expression of the replacement call
|
|
128
|
+
* @param state - The current abstract state before the evaluation of the replacement call
|
|
129
|
+
* @returns The abstract state after applying the abstract semantics of the replacement call
|
|
130
|
+
*/
|
|
131
|
+
protected abstract evalReplacementCall(call: DataflowGraphVertexFunctionCall, target: NodeId, source: NodeId, state: StateAbstractDomain<Domain>): StateAbstractDomain<Domain>;
|
|
132
|
+
/**
|
|
133
|
+
* Evaluates any access operation call visited by the abstract interpretation visitor by applying the abstract semantics of the access operation to the current abstract stat (e.g. for `$`, `[`, `[[`, ...).
|
|
134
|
+
* @param call - The data flow vertex of the access operation to evaluate
|
|
135
|
+
* @param state - The current abstract state before the evaluation of the access operation
|
|
136
|
+
* @returns The abstract state after applying the abstract semantics of the access operation
|
|
137
|
+
*/
|
|
138
|
+
protected abstract evalAccessCall(call: DataflowGraphVertexFunctionCall, state: StateAbstractDomain<Domain>): StateAbstractDomain<Domain>;
|
|
139
|
+
/** Gets all AST nodes for the predecessor vertices that are leaf nodes and exit vertices */
|
|
140
|
+
protected getPredecessorNodes(vertexId: NodeId): NodeId[];
|
|
141
|
+
/** Gets each variable origin that has already been visited and whose assignment has already been processed */
|
|
142
|
+
protected getVariableOrigins(nodeId: NodeId): NodeId[];
|
|
143
|
+
/** We only perform widening at `for`, `while`, or `repeat` loops with more than one incoming CFG edge */
|
|
144
|
+
protected isWideningPoint(nodeId: NodeId): boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Checks whether to continue visiting the control flow graph after a widening point.
|
|
147
|
+
* By default, we only continue visiting if the widening point is visited for the first time or the abstract state at the widening point changed.
|
|
148
|
+
*/
|
|
149
|
+
protected shouldContinueVisiting(wideningPoint: CfgSimpleVertex, oldState: StateAbstractDomain<Domain>): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Checks whether a control flow graph vertex should be skipped during visitation.
|
|
152
|
+
* By default, we only process vertices of leaf nodes and exit vertices (no entry nodes of complex nodes).
|
|
153
|
+
*/
|
|
154
|
+
protected shouldSkipVertex(vertex: CfgSimpleVertex): boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Whether widening should be performed at a widening point.
|
|
157
|
+
* By default, we perform widening when the number of visitation of the widening point reaches the widening threshold of the config.
|
|
158
|
+
*/
|
|
159
|
+
protected shouldWiden(wideningPoint: CfgSimpleVertex): boolean;
|
|
160
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AbstractInterpretationVisitor = void 0;
|
|
4
|
+
const control_flow_graph_1 = require("../control-flow/control-flow-graph");
|
|
5
|
+
const semantic_cfg_guided_visitor_1 = require("../control-flow/semantic-cfg-guided-visitor");
|
|
6
|
+
const vertex_1 = require("../dataflow/graph/vertex");
|
|
7
|
+
const dfg_get_origin_1 = require("../dataflow/origin/dfg-get-origin");
|
|
8
|
+
const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
9
|
+
const type_1 = require("../r-bridge/lang-4.x/ast/model/type");
|
|
10
|
+
const assert_1 = require("../util/assert");
|
|
11
|
+
const abstract_domain_1 = require("./domains/abstract-domain");
|
|
12
|
+
/**
|
|
13
|
+
* A control flow graph visitor to perform abstract interpretation.
|
|
14
|
+
*
|
|
15
|
+
* However, the visitor does not yet support inter-procedural abstract interpretation and abstract condition semantics.
|
|
16
|
+
*/
|
|
17
|
+
class AbstractInterpretationVisitor extends semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor {
|
|
18
|
+
/**
|
|
19
|
+
* The abstract trace of the abstract interpretation visitor mapping node IDs to the abstract state at the respective node.
|
|
20
|
+
*/
|
|
21
|
+
trace = new Map();
|
|
22
|
+
/**
|
|
23
|
+
* A set of nodes representing variable definitions that have already been visited but whose assignment has not yet been processed.
|
|
24
|
+
*/
|
|
25
|
+
unassigned = new Set();
|
|
26
|
+
/**
|
|
27
|
+
* The current abstract state domain at the currently processed AST node.
|
|
28
|
+
*/
|
|
29
|
+
currentState;
|
|
30
|
+
constructor(config) {
|
|
31
|
+
super({ ...config, defaultVisitingOrder: 'forward', defaultVisitingType: 'exit' });
|
|
32
|
+
this.currentState = config.domain.bottom();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolves the inferred abstract value of an AST node.
|
|
36
|
+
* This requires that the abstract interpretation visitor has been completed, or at least started.
|
|
37
|
+
* @param id - The ID of the node to get the inferred value for
|
|
38
|
+
* @param state - An optional state abstract domain used to resolve the inferred abstract value (defaults to the state at the requested node)
|
|
39
|
+
* @returns The inferred abstract value of the node, or `undefined` if no value was inferred for the node
|
|
40
|
+
*/
|
|
41
|
+
getAbstractValue(id, state) {
|
|
42
|
+
const node = (id === undefined || typeof id === 'object') ? id : this.getNormalizedAst(id);
|
|
43
|
+
state ??= node !== undefined ? this.getAbstractState(node.info.id) : undefined;
|
|
44
|
+
if (node === undefined) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
else if (state?.has(node.info.id)) {
|
|
48
|
+
return state.get(node.info.id);
|
|
49
|
+
}
|
|
50
|
+
const vertex = this.getDataflowGraph(node.info.id);
|
|
51
|
+
const call = vertex?.tag === vertex_1.VertexType.FunctionCall ? vertex : undefined;
|
|
52
|
+
const origins = Array.isArray(call?.origin) ? call.origin : [];
|
|
53
|
+
if (node.type === type_1.RType.Symbol) {
|
|
54
|
+
const values = this.getVariableOrigins(node.info.id).map(origin => state?.get(origin));
|
|
55
|
+
if (values.length > 0 && values.every(assert_1.isNotUndefined)) {
|
|
56
|
+
return abstract_domain_1.AbstractDomain.joinAll(values);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (node.type === type_1.RType.Argument && node.value !== undefined) {
|
|
60
|
+
return this.getAbstractValue(node.value, state);
|
|
61
|
+
}
|
|
62
|
+
else if (node.type === type_1.RType.ExpressionList && node.children.length > 0) {
|
|
63
|
+
return this.getAbstractValue(node.children[node.children.length - 1], state);
|
|
64
|
+
}
|
|
65
|
+
else if (origins.includes('builtin:pipe')) {
|
|
66
|
+
if (node.type === type_1.RType.Pipe || node.type === type_1.RType.BinaryOp) {
|
|
67
|
+
return this.getAbstractValue(node.rhs, state);
|
|
68
|
+
}
|
|
69
|
+
else if (call?.args.length === 2 && call?.args[1] !== r_function_call_1.EmptyArgument) {
|
|
70
|
+
return this.getAbstractValue(call.args[1].nodeId, state);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (origins.includes('builtin:if-then-else')) {
|
|
74
|
+
let values = [];
|
|
75
|
+
if (node.type === type_1.RType.IfThenElse && node.otherwise !== undefined) {
|
|
76
|
+
values = [node.then, node.otherwise].map(entry => this.getAbstractValue(entry, state));
|
|
77
|
+
}
|
|
78
|
+
else if (call?.args.every(arg => arg !== r_function_call_1.EmptyArgument) && call.args.length === 3) {
|
|
79
|
+
values = call.args.slice(1, 3).map(entry => this.getAbstractValue(entry.nodeId, state));
|
|
80
|
+
}
|
|
81
|
+
if (values.length > 0 && values.every(assert_1.isNotUndefined)) {
|
|
82
|
+
return abstract_domain_1.AbstractDomain.joinAll(values);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Gets the inferred abstract state at the location of a specific AST node.
|
|
88
|
+
* This requires that the abstract interpretation visitor has been completed, or at least started.
|
|
89
|
+
* @param id - The ID of the node to get the abstract state at
|
|
90
|
+
* @returns The abstract state at the node, or `undefined` if the node has no abstract state (i.e. the node has not been visited or is unreachable).
|
|
91
|
+
*/
|
|
92
|
+
getAbstractState(id) {
|
|
93
|
+
return id !== undefined ? this.trace.get(id) : undefined;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Gets the inferred abstract state at the end of the program (exit nodes of the control flow graph).
|
|
97
|
+
* This requires that the abstract interpretation visitor has been completed, or at least started.
|
|
98
|
+
* @returns The inferred abstract state at the end of the program
|
|
99
|
+
*/
|
|
100
|
+
getEndState() {
|
|
101
|
+
const exitPoints = this.config.controlFlow.exitPoints.map(id => this.getCfgVertex(id)).filter(assert_1.isNotUndefined);
|
|
102
|
+
const exitNodes = exitPoints.map(vertex => (0, control_flow_graph_1.getVertexRootId)(vertex)).filter(assert_1.isNotUndefined);
|
|
103
|
+
const states = exitNodes.map(node => this.getAbstractState(node)).filter(assert_1.isNotUndefined);
|
|
104
|
+
return this.config.domain.bottom().joinAll(states);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Gets the inferred abstract trace mapping AST nodes to the inferred abstract state at the respective node.
|
|
108
|
+
* @returns The inferred abstract trace of the program
|
|
109
|
+
*/
|
|
110
|
+
getAbstractTrace() {
|
|
111
|
+
return this.trace;
|
|
112
|
+
}
|
|
113
|
+
start() {
|
|
114
|
+
(0, assert_1.guard)(this.trace.size === 0, 'Abstract interpretation visitor has already been started');
|
|
115
|
+
super.start();
|
|
116
|
+
this.unassigned.clear();
|
|
117
|
+
}
|
|
118
|
+
visitNode(vertexId) {
|
|
119
|
+
const vertex = this.getCfgVertex(vertexId);
|
|
120
|
+
if (vertex === undefined) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
const nodeId = (0, control_flow_graph_1.getVertexRootId)(vertex);
|
|
124
|
+
if (this.isWideningPoint(nodeId)) {
|
|
125
|
+
// only check widening points at the entry vertex
|
|
126
|
+
if (vertex.type === control_flow_graph_1.CfgVertexType.EndMarker) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
const oldState = this.getAbstractState(nodeId) ?? this.config.domain.bottom();
|
|
130
|
+
const predecessorDomains = this.getPredecessorNodes(vertex.id).map(pred => this.getAbstractState(pred)).filter(assert_1.isNotUndefined);
|
|
131
|
+
this.currentState = this.config.domain.bottom().joinAll(predecessorDomains);
|
|
132
|
+
if (this.shouldWiden(vertex)) {
|
|
133
|
+
this.currentState = oldState.widen(this.currentState);
|
|
134
|
+
}
|
|
135
|
+
this.trace.set(nodeId, this.currentState);
|
|
136
|
+
return this.shouldContinueVisiting(vertex, oldState);
|
|
137
|
+
}
|
|
138
|
+
else if (this.shouldSkipVertex(vertex)) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
const predecessorDomains = this.getPredecessorNodes(vertex.id).map(pred => this.getAbstractState(pred)).filter(assert_1.isNotUndefined);
|
|
142
|
+
this.currentState = this.config.domain.bottom().joinAll(predecessorDomains);
|
|
143
|
+
this.onVisitNode(vertexId);
|
|
144
|
+
// discard the inferred abstract state when encountering functions with unknown side effects (e.g. `eval`)
|
|
145
|
+
if (this.config.dfg.unknownSideEffects.has(nodeId)) {
|
|
146
|
+
this.currentState = this.currentState.bottom();
|
|
147
|
+
}
|
|
148
|
+
this.trace.set(nodeId, this.currentState);
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
onVariableDefinition({ vertex }) {
|
|
152
|
+
if (this.getAbstractState(vertex.id) === undefined) {
|
|
153
|
+
this.unassigned.add(vertex.id);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
onAssignmentCall({ target, source }) {
|
|
157
|
+
if (target === undefined || source === undefined) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const value = this.getAbstractValue(source);
|
|
161
|
+
this.unassigned.delete(target);
|
|
162
|
+
if (value !== undefined) {
|
|
163
|
+
this.currentState.set(target, value);
|
|
164
|
+
this.trace.set(target, this.currentState.create(this.currentState.value));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
onReplacementCall({ call, target, source }) {
|
|
168
|
+
if (source === undefined || target === undefined) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
this.currentState = this.evalReplacementCall(call, target, source, this.currentState);
|
|
172
|
+
this.unassigned.delete(target);
|
|
173
|
+
}
|
|
174
|
+
onAccessCall({ call }) {
|
|
175
|
+
this.currentState = this.evalAccessCall(call, this.currentState);
|
|
176
|
+
}
|
|
177
|
+
onUnnamedCall({ call }) {
|
|
178
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
179
|
+
}
|
|
180
|
+
onEvalFunctionCall({ call }) {
|
|
181
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
182
|
+
}
|
|
183
|
+
onApplyFunctionCall({ call }) {
|
|
184
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
185
|
+
}
|
|
186
|
+
onSourceCall({ call }) {
|
|
187
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
188
|
+
}
|
|
189
|
+
onGetCall({ call }) {
|
|
190
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
191
|
+
}
|
|
192
|
+
onRmCall({ call }) {
|
|
193
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
194
|
+
}
|
|
195
|
+
onListCall({ call }) {
|
|
196
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
197
|
+
}
|
|
198
|
+
onVectorCall({ call }) {
|
|
199
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
200
|
+
}
|
|
201
|
+
onSpecialBinaryOpCall({ call }) {
|
|
202
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
203
|
+
}
|
|
204
|
+
onQuoteCall({ call }) {
|
|
205
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
206
|
+
}
|
|
207
|
+
onLibraryCall({ call }) {
|
|
208
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
209
|
+
}
|
|
210
|
+
onDefaultFunctionCall({ call }) {
|
|
211
|
+
this.currentState = this.evalFunctionCall(call, this.currentState);
|
|
212
|
+
}
|
|
213
|
+
/** Gets all AST nodes for the predecessor vertices that are leaf nodes and exit vertices */
|
|
214
|
+
getPredecessorNodes(vertexId) {
|
|
215
|
+
return this.config.controlFlow.graph.outgoingEdges(vertexId)?.keys() // outgoing dependency edges are incoming CFG edges
|
|
216
|
+
.map(id => this.getCfgVertex(id))
|
|
217
|
+
.flatMap(vertex => {
|
|
218
|
+
if (vertex === undefined) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
else if (this.shouldSkipVertex(vertex)) {
|
|
222
|
+
return this.getPredecessorNodes(vertex.id);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
return [(0, control_flow_graph_1.getVertexRootId)(vertex)];
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
.toArray() ?? [];
|
|
229
|
+
}
|
|
230
|
+
/** Gets each variable origin that has already been visited and whose assignment has already been processed */
|
|
231
|
+
getVariableOrigins(nodeId) {
|
|
232
|
+
return (0, dfg_get_origin_1.getOriginInDfg)(this.config.dfg, nodeId)
|
|
233
|
+
?.filter(origin => origin.type === 0 /* OriginType.ReadVariableOrigin */)
|
|
234
|
+
.map(origin => origin.id)
|
|
235
|
+
.filter(origin => this.trace.has(origin) && !this.unassigned.has(origin)) ?? [];
|
|
236
|
+
}
|
|
237
|
+
/** We only perform widening at `for`, `while`, or `repeat` loops with more than one incoming CFG edge */
|
|
238
|
+
isWideningPoint(nodeId) {
|
|
239
|
+
const incomingEdges = this.config.controlFlow.graph.outgoingEdges(nodeId)?.size; // outgoing dependency edges are incoming CFG edges
|
|
240
|
+
if (incomingEdges === undefined || incomingEdges <= 1) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
const node = this.getNormalizedAst(nodeId);
|
|
244
|
+
if (node?.type === type_1.RType.ForLoop || node?.type === type_1.RType.WhileLoop || node?.type === type_1.RType.RepeatLoop) {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
const dataflowVertex = this.getDataflowGraph(nodeId);
|
|
248
|
+
if (dataflowVertex?.tag !== vertex_1.VertexType.FunctionCall || !Array.isArray(dataflowVertex.origin)) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
const origin = dataflowVertex.origin;
|
|
252
|
+
return origin.includes('builtin:for-loop') || origin.includes('builtin:while-loop') || origin.includes('builtin:repeat-loop');
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Checks whether to continue visiting the control flow graph after a widening point.
|
|
256
|
+
* By default, we only continue visiting if the widening point is visited for the first time or the abstract state at the widening point changed.
|
|
257
|
+
*/
|
|
258
|
+
shouldContinueVisiting(wideningPoint, oldState) {
|
|
259
|
+
const visitedCount = this.visited.get(wideningPoint.id) ?? 0;
|
|
260
|
+
this.visited.set(wideningPoint.id, visitedCount + 1);
|
|
261
|
+
return visitedCount === 0 || !oldState.equals(this.currentState);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Checks whether a control flow graph vertex should be skipped during visitation.
|
|
265
|
+
* By default, we only process vertices of leaf nodes and exit vertices (no entry nodes of complex nodes).
|
|
266
|
+
*/
|
|
267
|
+
shouldSkipVertex(vertex) {
|
|
268
|
+
return vertex.type !== control_flow_graph_1.CfgVertexType.EndMarker && vertex.end !== undefined;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Whether widening should be performed at a widening point.
|
|
272
|
+
* By default, we perform widening when the number of visitation of the widening point reaches the widening threshold of the config.
|
|
273
|
+
*/
|
|
274
|
+
shouldWiden(wideningPoint) {
|
|
275
|
+
return (this.visited.get(wideningPoint.id) ?? 0) >= this.config.ctx.config.abstractInterpretation.wideningThreshold;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
exports.AbstractInterpretationVisitor = AbstractInterpretationVisitor;
|
|
279
|
+
//# sourceMappingURL=absint-visitor.js.map
|
|
@@ -2,7 +2,6 @@ import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-i
|
|
|
2
2
|
import type { AbstractDomainValue } from '../domains/abstract-domain';
|
|
3
3
|
import { PosIntervalDomain } from '../domains/positive-interval-domain';
|
|
4
4
|
import { ProductDomain } from '../domains/product-domain';
|
|
5
|
-
import type { SetRangeLimit } from '../domains/set-range-domain';
|
|
6
5
|
import { SetRangeDomain } from '../domains/set-range-domain';
|
|
7
6
|
import { StateAbstractDomain } from '../domains/state-abstract-domain';
|
|
8
7
|
/** The type of the abstract product representing the shape of data frames */
|
|
@@ -17,7 +16,7 @@ export type DataFrameShapeProperty<Property extends keyof AbstractDataFrameShape
|
|
|
17
16
|
* The data frame abstract domain as product domain of a column names domain, column count domain, and row count domain.
|
|
18
17
|
*/
|
|
19
18
|
export declare class DataFrameDomain extends ProductDomain<AbstractDataFrameShape> {
|
|
20
|
-
constructor(value: AbstractDataFrameShape
|
|
19
|
+
constructor(value: AbstractDataFrameShape);
|
|
21
20
|
create(value: AbstractDataFrameShape): this;
|
|
22
21
|
/**
|
|
23
22
|
* The current abstract value of the column names domain.
|
|
@@ -33,6 +32,7 @@ export declare class DataFrameDomain extends ProductDomain<AbstractDataFrameShap
|
|
|
33
32
|
get rows(): AbstractDataFrameShape['rows'];
|
|
34
33
|
static bottom(maxColNames?: number): DataFrameDomain;
|
|
35
34
|
static top(maxColNames?: number): DataFrameDomain;
|
|
35
|
+
private static refine;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
38
|
* The data frame state abstract domain as state domain mapping AST node IDs to inferred abstract data frame shapes.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DataFrameStateDomain = exports.DataFrameDomain = void 0;
|
|
4
|
+
const lattice_1 = require("../domains/lattice");
|
|
4
5
|
const positive_interval_domain_1 = require("../domains/positive-interval-domain");
|
|
5
6
|
const product_domain_1 = require("../domains/product-domain");
|
|
6
7
|
const set_range_domain_1 = require("../domains/set-range-domain");
|
|
@@ -9,15 +10,15 @@ const state_abstract_domain_1 = require("../domains/state-abstract-domain");
|
|
|
9
10
|
* The data frame abstract domain as product domain of a column names domain, column count domain, and row count domain.
|
|
10
11
|
*/
|
|
11
12
|
class DataFrameDomain extends product_domain_1.ProductDomain {
|
|
12
|
-
constructor(value
|
|
13
|
-
super({
|
|
14
|
-
colnames:
|
|
15
|
-
cols:
|
|
16
|
-
rows:
|
|
17
|
-
});
|
|
13
|
+
constructor(value) {
|
|
14
|
+
super(DataFrameDomain.refine({
|
|
15
|
+
colnames: value.colnames.create(value.colnames.value),
|
|
16
|
+
cols: value.cols.create(value.cols.value),
|
|
17
|
+
rows: value.rows.create(value.rows.value)
|
|
18
|
+
}));
|
|
18
19
|
}
|
|
19
20
|
create(value) {
|
|
20
|
-
return new DataFrameDomain(value
|
|
21
|
+
return new DataFrameDomain(value);
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
23
24
|
* The current abstract value of the column names domain.
|
|
@@ -51,6 +52,21 @@ class DataFrameDomain extends product_domain_1.ProductDomain {
|
|
|
51
52
|
rows: positive_interval_domain_1.PosIntervalDomain.top()
|
|
52
53
|
});
|
|
53
54
|
}
|
|
55
|
+
static refine(value) {
|
|
56
|
+
if (value.colnames.isValue() && value.cols.isValue()) {
|
|
57
|
+
if (value.colnames.value.range === lattice_1.Top && value.colnames.value.min.size >= value.cols.value[1]) {
|
|
58
|
+
value.colnames = value.colnames.meet({ min: new Set(), range: value.colnames.value.min });
|
|
59
|
+
}
|
|
60
|
+
if (value.colnames.isValue()) {
|
|
61
|
+
const minColNames = value.colnames.value.min.size;
|
|
62
|
+
const maxColNames = value.colnames.isFinite() ? value.colnames.value.min.size + value.colnames.value.range.size : Infinity;
|
|
63
|
+
if (minColNames > value.cols.value[0] || maxColNames < value.cols.value[1]) {
|
|
64
|
+
value.cols = value.cols.meet([minColNames, maxColNames]);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
54
70
|
}
|
|
55
71
|
exports.DataFrameDomain = DataFrameDomain;
|
|
56
72
|
/**
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import type { DataflowGraph } from '../../../dataflow/graph/graph';
|
|
2
|
+
import type { ReadOnlyFlowrAnalyzerContext } from '../../../project/context/flowr-analyzer-context';
|
|
2
3
|
import type { RNode } from '../../../r-bridge/lang-4.x/ast/model/model';
|
|
3
4
|
import type { RAccess, RNamedAccess } from '../../../r-bridge/lang-4.x/ast/model/nodes/r-access';
|
|
4
5
|
import type { ParentInformation } from '../../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
5
|
-
import type {
|
|
6
|
-
import type { ReadOnlyFlowrAnalyzerContext } from '../../../project/context/flowr-analyzer-context';
|
|
6
|
+
import type { DataFrameOperations, DataFrameShapeInferenceVisitor } from '../shape-inference';
|
|
7
7
|
/**
|
|
8
|
-
* Maps a concrete data frame access to abstract data frame operations.
|
|
8
|
+
* Maps a concrete data frame access operation to abstract data frame operations.
|
|
9
9
|
* @param node - The R node of the access
|
|
10
10
|
* @param dfg - The data flow graph for resolving the arguments
|
|
11
|
-
* @param ctx - The
|
|
12
|
-
* @returns
|
|
11
|
+
* @param ctx - The current flowR analyzer context
|
|
12
|
+
* @returns The mapped abstract data frame operations for the access operation, or `undefined` if the node does not represent a data frame access operation
|
|
13
13
|
*/
|
|
14
|
-
export declare function mapDataFrameAccess(node: RNode<ParentInformation>, dfg: DataflowGraph, ctx: ReadOnlyFlowrAnalyzerContext):
|
|
14
|
+
export declare function mapDataFrameAccess(node: RNode<ParentInformation>, inference: DataFrameShapeInferenceVisitor, dfg: DataflowGraph, ctx: ReadOnlyFlowrAnalyzerContext): DataFrameOperations;
|
|
15
15
|
/**
|
|
16
16
|
* Checks whether an access node represents a string-based access (`$` or `@`), and no index-based access (`[` or `[[`).
|
|
17
17
|
*/
|