@eagleoutice/flowr 2.2.16 → 2.3.0

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.
Files changed (78) hide show
  1. package/README.md +35 -19
  2. package/abstract-interpretation/data-frame/absint-info.d.ts +109 -0
  3. package/abstract-interpretation/data-frame/absint-info.js +31 -0
  4. package/abstract-interpretation/data-frame/absint-visitor.d.ts +59 -0
  5. package/abstract-interpretation/data-frame/absint-visitor.js +173 -0
  6. package/abstract-interpretation/data-frame/domain.d.ts +107 -0
  7. package/abstract-interpretation/data-frame/domain.js +315 -0
  8. package/abstract-interpretation/data-frame/mappers/access-mapper.d.ts +17 -0
  9. package/abstract-interpretation/data-frame/mappers/access-mapper.js +166 -0
  10. package/abstract-interpretation/data-frame/mappers/arguments.d.ts +117 -0
  11. package/abstract-interpretation/data-frame/mappers/arguments.js +188 -0
  12. package/abstract-interpretation/data-frame/mappers/assignment-mapper.d.ts +20 -0
  13. package/abstract-interpretation/data-frame/mappers/assignment-mapper.js +34 -0
  14. package/abstract-interpretation/data-frame/mappers/function-mapper.d.ts +261 -0
  15. package/abstract-interpretation/data-frame/mappers/function-mapper.js +1219 -0
  16. package/abstract-interpretation/data-frame/mappers/replacement-mapper.d.ts +12 -0
  17. package/abstract-interpretation/data-frame/mappers/replacement-mapper.js +206 -0
  18. package/abstract-interpretation/data-frame/resolve-args.d.ts +42 -0
  19. package/abstract-interpretation/data-frame/resolve-args.js +118 -0
  20. package/abstract-interpretation/data-frame/semantics.d.ts +213 -0
  21. package/abstract-interpretation/data-frame/semantics.js +366 -0
  22. package/abstract-interpretation/data-frame/shape-inference.d.ts +38 -0
  23. package/abstract-interpretation/data-frame/shape-inference.js +117 -0
  24. package/benchmark/slicer.d.ts +15 -1
  25. package/benchmark/slicer.js +135 -0
  26. package/benchmark/stats/print.js +123 -45
  27. package/benchmark/stats/size-of.d.ts +7 -0
  28. package/benchmark/stats/size-of.js +1 -0
  29. package/benchmark/stats/stats.d.ts +30 -1
  30. package/benchmark/stats/stats.js +4 -2
  31. package/benchmark/summarizer/data.d.ts +33 -2
  32. package/benchmark/summarizer/first-phase/input.js +5 -1
  33. package/benchmark/summarizer/first-phase/process.js +47 -1
  34. package/benchmark/summarizer/second-phase/process.js +101 -3
  35. package/cli/benchmark-app.d.ts +1 -0
  36. package/cli/benchmark-app.js +1 -0
  37. package/cli/benchmark-helper-app.d.ts +1 -0
  38. package/cli/benchmark-helper-app.js +8 -2
  39. package/cli/common/options.js +2 -0
  40. package/config.d.ts +31 -0
  41. package/config.js +21 -1
  42. package/control-flow/control-flow-graph.d.ts +1 -0
  43. package/control-flow/control-flow-graph.js +4 -0
  44. package/control-flow/dfg-cfg-guided-visitor.js +1 -1
  45. package/control-flow/semantic-cfg-guided-visitor.d.ts +1 -1
  46. package/control-flow/semantic-cfg-guided-visitor.js +1 -1
  47. package/dataflow/environments/built-in.d.ts +5 -3
  48. package/dataflow/environments/built-in.js +3 -1
  49. package/dataflow/eval/resolve/alias-tracking.js +2 -2
  50. package/dataflow/eval/resolve/resolve.d.ts +53 -9
  51. package/dataflow/eval/resolve/resolve.js +132 -38
  52. package/dataflow/internal/process/functions/call/built-in/built-in-source.d.ts +1 -0
  53. package/dataflow/internal/process/functions/call/built-in/built-in-source.js +4 -0
  54. package/documentation/doc-util/doc-query.js +10 -0
  55. package/documentation/print-interface-wiki.js +11 -0
  56. package/documentation/print-linter-wiki.js +4 -0
  57. package/documentation/print-query-wiki.js +17 -0
  58. package/linter/linter-rules.d.ts +25 -2
  59. package/linter/linter-rules.js +3 -1
  60. package/linter/rules/absolute-path.d.ts +1 -1
  61. package/linter/rules/dataframe-access-validation.d.ts +53 -0
  62. package/linter/rules/dataframe-access-validation.js +116 -0
  63. package/linter/rules/naming-convention.d.ts +1 -1
  64. package/linter/rules/naming-convention.js +5 -1
  65. package/package.json +2 -2
  66. package/queries/catalog/df-shape-query/df-shape-query-executor.d.ts +3 -0
  67. package/queries/catalog/df-shape-query/df-shape-query-executor.js +46 -0
  68. package/queries/catalog/df-shape-query/df-shape-query-format.d.ts +72 -0
  69. package/queries/catalog/df-shape-query/df-shape-query-format.js +31 -0
  70. package/queries/query.d.ts +61 -1
  71. package/queries/query.js +2 -0
  72. package/util/files.d.ts +8 -2
  73. package/util/files.js +22 -4
  74. package/util/r-value.d.ts +23 -0
  75. package/util/r-value.js +113 -0
  76. package/util/version.js +1 -1
  77. package/util/cfg/cfg.d.ts +0 -0
  78. package/util/cfg/cfg.js +0 -2
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.2.15, R v4.5.0 (r-shell engine)
27
+ flowR repl using flowR v2.2.16, R v4.5.0 (r-shell engine)
28
28
  R> :query @linter "read.csv(\"/root/x.txt\")"
29
29
  ```
30
30
 
@@ -50,6 +50,8 @@ It offers a wide variety of features, for example:
50
50
  ╰ _Metadata_: <code>{"totalConsidered":0,"searchTimeMs":0,"processTimeMs":0}</code>
51
51
  ╰ **Naming Convention** (naming-convention):
52
52
  ╰ _Metadata_: <code>{"numMatches":0,"numBreak":0,"searchTimeMs":0,"processTimeMs":0}</code>
53
+ ╰ **Dataframe Access Validation** (dataframe-access-validation):
54
+ ╰ _Metadata_: <code>{"numOperations":0,"numAccesses":0,"totalAccessed":0,"searchTimeMs":0,"processTimeMs":0}</code>
53
55
  All queries together required ≈2 ms (1ms accuracy, total 7 ms)
54
56
  ```
55
57
 
@@ -66,32 +68,36 @@ It offers a wide variety of features, for example:
66
68
  ```
67
69
 
68
70
 
71
+ (This query can be shortened to `@linter` when used within the REPL command <span title="Description (Repl Command): Query the given R code, start with 'file://' to indicate a file. The query is to be a valid query in json format (use 'help' to get more information).">`:query`</span>).
72
+
69
73
 
70
74
 
71
75
  _Results (prettified and summarized):_
72
76
 
73
- Query: **linter** (10 ms)\
77
+ Query: **linter** (12 ms)\
74
78
  &nbsp;&nbsp;&nbsp;╰ **Deprecated Functions** (deprecated-functions):\
75
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"totalDeprecatedCalls":0,"totalDeprecatedFunctionDefinitions":0,"searchTimeMs":2,"processTimeMs":0}</code>\
79
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"totalDeprecatedCalls":0,"totalDeprecatedFunctionDefinitions":0,"searchTimeMs":1,"processTimeMs":0}</code>\
76
80
  &nbsp;&nbsp;&nbsp;╰ **File Path Validity** (file-path-validity):\
77
81
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ definitely:\
78
82
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
79
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"totalReads":1,"totalUnknown":0,"totalWritesBeforeAlways":0,"totalValid":0,"searchTimeMs":4,"processTimeMs":1}</code>\
83
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"totalReads":1,"totalUnknown":0,"totalWritesBeforeAlways":0,"totalValid":0,"searchTimeMs":3,"processTimeMs":1}</code>\
80
84
  &nbsp;&nbsp;&nbsp;╰ **Seeded Randomness** (seeded-randomness):\
81
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"consumerCalls":0,"callsWithFunctionProducers":0,"callsWithAssignmentProducers":0,"callsWithNonConstantProducers":0,"searchTimeMs":0,"processTimeMs":0}</code>\
85
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"consumerCalls":0,"callsWithFunctionProducers":0,"callsWithAssignmentProducers":0,"callsWithNonConstantProducers":0,"searchTimeMs":1,"processTimeMs":0}</code>\
82
86
  &nbsp;&nbsp;&nbsp;╰ **Absolute Paths** (absolute-file-paths):\
83
87
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ definitely:\
84
88
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
85
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"totalConsidered":1,"totalUnknown":0,"searchTimeMs":1,"processTimeMs":1}</code>\
89
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"totalConsidered":1,"totalUnknown":0,"searchTimeMs":2,"processTimeMs":0}</code>\
86
90
  &nbsp;&nbsp;&nbsp;╰ **Unused Definitions** (unused-definitions):\
87
91
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"totalConsidered":0,"searchTimeMs":0,"processTimeMs":0}</code>\
88
92
  &nbsp;&nbsp;&nbsp;╰ **Naming Convention** (naming-convention):\
89
93
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"numMatches":0,"numBreak":0,"searchTimeMs":0,"processTimeMs":0}</code>\
90
- _All queries together required ≈10 ms (1ms accuracy, total 208 ms)_
94
+ &nbsp;&nbsp;&nbsp;╰ **Dataframe Access Validation** (dataframe-access-validation):\
95
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"numOperations":0,"numAccesses":0,"totalAccessed":0,"searchTimeMs":0,"processTimeMs":3}</code>\
96
+ _All queries together required ≈13 ms (1ms accuracy, total 207 ms)_
91
97
 
92
98
  <details> <summary style="color:gray">Show Detailed Results as Json</summary>
93
99
 
94
- The analysis required _207.6 ms_ (including parsing and normalization and the query) within the generation environment.
100
+ The analysis required _207.2 ms_ (including parsing and normalization and the query) within the generation environment.
95
101
 
96
102
  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.
97
103
  Please consult the [Interface](https://github.com/flowr-analysis/flowr/wiki/Interface) wiki page for more information on how to get those.
@@ -108,7 +114,7 @@ It offers a wide variety of features, for example:
108
114
  ".meta": {
109
115
  "totalDeprecatedCalls": 0,
110
116
  "totalDeprecatedFunctionDefinitions": 0,
111
- "searchTimeMs": 2,
117
+ "searchTimeMs": 1,
112
118
  "processTimeMs": 0
113
119
  }
114
120
  },
@@ -130,7 +136,7 @@ It offers a wide variety of features, for example:
130
136
  "totalUnknown": 0,
131
137
  "totalWritesBeforeAlways": 0,
132
138
  "totalValid": 0,
133
- "searchTimeMs": 4,
139
+ "searchTimeMs": 3,
134
140
  "processTimeMs": 1
135
141
  }
136
142
  },
@@ -141,7 +147,7 @@ It offers a wide variety of features, for example:
141
147
  "callsWithFunctionProducers": 0,
142
148
  "callsWithAssignmentProducers": 0,
143
149
  "callsWithNonConstantProducers": 0,
144
- "searchTimeMs": 0,
150
+ "searchTimeMs": 1,
145
151
  "processTimeMs": 0
146
152
  }
147
153
  },
@@ -161,8 +167,8 @@ It offers a wide variety of features, for example:
161
167
  ".meta": {
162
168
  "totalConsidered": 1,
163
169
  "totalUnknown": 0,
164
- "searchTimeMs": 1,
165
- "processTimeMs": 1
170
+ "searchTimeMs": 2,
171
+ "processTimeMs": 0
166
172
  }
167
173
  },
168
174
  "unused-definitions": {
@@ -181,14 +187,24 @@ It offers a wide variety of features, for example:
181
187
  "searchTimeMs": 0,
182
188
  "processTimeMs": 0
183
189
  }
190
+ },
191
+ "dataframe-access-validation": {
192
+ "results": [],
193
+ ".meta": {
194
+ "numOperations": 0,
195
+ "numAccesses": 0,
196
+ "totalAccessed": 0,
197
+ "searchTimeMs": 0,
198
+ "processTimeMs": 3
199
+ }
184
200
  }
185
201
  },
186
202
  ".meta": {
187
- "timing": 10
203
+ "timing": 12
188
204
  }
189
205
  },
190
206
  ".meta": {
191
- "timing": 10
207
+ "timing": 13
192
208
  }
193
209
  }
194
210
  ```
@@ -255,7 +271,7 @@ It offers a wide variety of features, for example:
255
271
 
256
272
  ```shell
257
273
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
258
- flowR repl using flowR v2.2.15, R v4.5.0 (r-shell engine)
274
+ flowR repl using flowR v2.2.16, R v4.5.0 (r-shell engine)
259
275
  R> :slicer test/testfiles/example.R --criterion "11@sum"
260
276
  ```
261
277
 
@@ -302,7 +318,7 @@ It offers a wide variety of features, for example:
302
318
 
303
319
 
304
320
  * 🚀 **fast data- and control-flow graphs**\
305
- Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">136.2 ms</span></i> (as of Jun 2, 2025),
321
+ Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">136.1 ms</span></i> (as of Jul 12, 2025),
306
322
  _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,
307
323
  and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/Dataflow-Graph) for more details on the dataflow graph.
308
324
 
@@ -338,7 +354,7 @@ It offers a wide variety of features, for example:
338
354
 
339
355
  ```shell
340
356
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
341
- flowR repl using flowR v2.2.15, R v4.5.0 (r-shell engine)
357
+ flowR repl using flowR v2.2.16, R v4.5.0 (r-shell engine)
342
358
  R> :dataflow* test/testfiles/example.R
343
359
  ```
344
360
 
@@ -639,7 +655,7 @@ It offers a wide variety of features, for example:
639
655
  ```
640
656
 
641
657
 
642
- (The analysis required _13.9 ms_ (including parse and normalize, using the [r-shell](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
658
+ (The analysis required _13.8 ms_ (including parse and normalize, using the [r-shell](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
643
659
 
644
660
 
645
661
 
@@ -0,0 +1,109 @@
1
+ import type { RNode } from '../../r-bridge/lang-4.x/ast/model/model';
2
+ import type { ParentInformation } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
3
+ import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
4
+ import type { DataFrameStateDomain } from './domain';
5
+ import type { ConstraintType, DataFrameOperationArgs, DataFrameOperationName, DataFrameOperationOptions } from './semantics';
6
+ /**
7
+ * An abstract data frame operation without additional options.
8
+ * - `operation` contains the type of the abstract operation (see {@link DataFrameOperationName})
9
+ * - `operand` contains the ID of the data frame operand of the operation (may be `undefined`)
10
+ * - `...args` contains the arguments of the abstract operation (see {@link DataFrameOperationArgs})
11
+ */
12
+ export type DataFrameOperationType<OperationName extends DataFrameOperationName = DataFrameOperationName> = {
13
+ [Name in OperationName]: {
14
+ operation: Name;
15
+ operand: NodeId | undefined;
16
+ } & DataFrameOperationArgs<Name>;
17
+ }[OperationName];
18
+ /**
19
+ * An abstract data frame operation.
20
+ * - `operation` contains the type of the abstract operation (see {@link DataFrameOperationName})
21
+ * - `operand` contains the ID of the data frame operand of the operation (may be `undefined`)
22
+ * - `type` optionally contains the constraint type to overwrite the default type of the operation (see {@link ConstraintType})
23
+ * - `options` optionally contains additional options for the abstract operation (see {@link DataFrameOperationOptions})
24
+ * - `...args` contains the arguments of the abstract operation (see {@link DataFrameOperationArgs})
25
+ */
26
+ export type DataFrameOperation<OperationName extends DataFrameOperationName = DataFrameOperationName> = {
27
+ [Name in OperationName]: {
28
+ operation: Name;
29
+ operand: NodeId | undefined;
30
+ type?: ConstraintType;
31
+ options?: DataFrameOperationOptions<Name>;
32
+ } & DataFrameOperationArgs<Name>;
33
+ }[OperationName];
34
+ /**
35
+ * Represents the base data frame information stored in the abstract interpretation info of an AST node.
36
+ * - `type` optionally defines the type of the extra information stored in the data frame info
37
+ * - `domain` contains the abstract data frame shape state of the node
38
+ * This may not be present if the data frame shape inference has not been executed yet or the program contains no data frames
39
+ */
40
+ interface DataFrameInfoBase {
41
+ type?: string;
42
+ domain?: DataFrameStateDomain;
43
+ }
44
+ /** Enum to mark nodes during the data frame shape inference */
45
+ export declare enum DataFrameInfoMarker {
46
+ /** Marks the target symbol of assignments as "unassigned" until the assigned expression is evaluated */
47
+ Unassigned = "unassigned"
48
+ }
49
+ /**
50
+ * Represents the data frame information for a node without extra data frame information,
51
+ * i.e. for all nodes that do not represent a data frame assignment or data frame operation (this is the default).
52
+ *
53
+ * The `marker` can be used to mark nodes during the data frame shape inference.
54
+ */
55
+ interface DataFrameEmptyInfo extends DataFrameInfoBase {
56
+ type?: never;
57
+ marker?: DataFrameInfoMarker;
58
+ }
59
+ /**
60
+ * Represents the data frame information for a data frame assignment with a target identifier (symbol/string) and an assigned expression.
61
+ * This is used during data frame shape inference to mark assignments of data frame expressions to an identifier.
62
+ *
63
+ * Use {@link hasDataFrameAssignmentInfo} to check whether an AST node has attached data frame assignment information.
64
+ */
65
+ export interface DataFrameAssignmentInfo extends DataFrameInfoBase {
66
+ type: 'assignment';
67
+ identifier: NodeId;
68
+ expression: NodeId;
69
+ }
70
+ /**
71
+ * Represents the data frame information for a data frame function/operation with mapped abstract operations.
72
+ * This is used during data frame shape inference to store the abstract operations a data frame function/operation is mapped to.
73
+ *
74
+ * The order of the abstract operations is the order in which their semantics are applied (for example, access operations are typically before other operations in the list).
75
+ * Moreover, abstract operations that take the result of previous abstract operation as data frame operand must have the `operand` set to `undefined`.
76
+ *
77
+ * Use {@link hasDataFrameExpressionInfo} to check whether an AST node has attached data frame expression information.
78
+ */
79
+ export interface DataFrameExpressionInfo extends DataFrameInfoBase {
80
+ type: 'expression';
81
+ operations: DataFrameOperation[];
82
+ }
83
+ /**
84
+ * Represents the data frame shape inference information stored in the abstract interpretation info of AST nodes.
85
+ */
86
+ export type DataFrameInfo = DataFrameEmptyInfo | DataFrameAssignmentInfo | DataFrameExpressionInfo;
87
+ /**
88
+ * Represents the abstract interpretation information attached to AST nodes.
89
+ */
90
+ export interface AbstractInterpretationInfo {
91
+ dataFrame?: DataFrameInfo;
92
+ }
93
+ /**
94
+ * Checks whether an AST node has attached data frame assignment information.
95
+ */
96
+ export declare function hasDataFrameAssignmentInfo<OtherInfo>(node: RNode<OtherInfo & ParentInformation & AbstractInterpretationInfo>): node is RNode<OtherInfo & ParentInformation & AbstractInterpretationInfo & {
97
+ dataFrame: DataFrameAssignmentInfo;
98
+ }>;
99
+ /**
100
+ * Checks whether an AST node has attached data frame expression information.
101
+ */
102
+ export declare function hasDataFrameExpressionInfo<OtherInfo>(node: RNode<OtherInfo & ParentInformation & AbstractInterpretationInfo>): node is RNode<OtherInfo & ParentInformation & AbstractInterpretationInfo & {
103
+ dataFrame: DataFrameExpressionInfo;
104
+ }>;
105
+ /**
106
+ * Checks whether an AST node has an attached data frame info marker.
107
+ */
108
+ export declare function hasDataFrameInfoMarker<OtherInfo>(node: RNode<OtherInfo & ParentInformation & AbstractInterpretationInfo>, marker: DataFrameInfoMarker): boolean;
109
+ export {};
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DataFrameInfoMarker = void 0;
4
+ exports.hasDataFrameAssignmentInfo = hasDataFrameAssignmentInfo;
5
+ exports.hasDataFrameExpressionInfo = hasDataFrameExpressionInfo;
6
+ exports.hasDataFrameInfoMarker = hasDataFrameInfoMarker;
7
+ /** Enum to mark nodes during the data frame shape inference */
8
+ var DataFrameInfoMarker;
9
+ (function (DataFrameInfoMarker) {
10
+ /** Marks the target symbol of assignments as "unassigned" until the assigned expression is evaluated */
11
+ DataFrameInfoMarker["Unassigned"] = "unassigned";
12
+ })(DataFrameInfoMarker || (exports.DataFrameInfoMarker = DataFrameInfoMarker = {}));
13
+ /**
14
+ * Checks whether an AST node has attached data frame assignment information.
15
+ */
16
+ function hasDataFrameAssignmentInfo(node) {
17
+ return node.info.dataFrame?.type === 'assignment';
18
+ }
19
+ /**
20
+ * Checks whether an AST node has attached data frame expression information.
21
+ */
22
+ function hasDataFrameExpressionInfo(node) {
23
+ return node.info.dataFrame?.type === 'expression';
24
+ }
25
+ /**
26
+ * Checks whether an AST node has an attached data frame info marker.
27
+ */
28
+ function hasDataFrameInfoMarker(node, marker) {
29
+ return node.info.dataFrame?.type === undefined && node.info.dataFrame?.marker === marker;
30
+ }
31
+ //# sourceMappingURL=absint-info.js.map
@@ -0,0 +1,59 @@
1
+ import type { CfgBasicBlockVertex, 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, DataflowGraphVertexVariableDefinition } from '../../dataflow/graph/vertex';
6
+ import type { NoInfo } from '../../r-bridge/lang-4.x/ast/model/model';
7
+ import type { NormalizedAst } 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 AbstractInterpretationInfo } from './absint-info';
10
+ export type DataFrameShapeInferenceVisitorConfiguration<OtherInfo = NoInfo, ControlFlow extends ControlFlowInformation = ControlFlowInformation, Ast extends NormalizedAst<OtherInfo & AbstractInterpretationInfo> = NormalizedAst<OtherInfo & AbstractInterpretationInfo>, Dfg extends DataflowGraph = DataflowGraph> = Omit<SemanticCfgGuidedVisitorConfiguration<OtherInfo & AbstractInterpretationInfo, ControlFlow, Ast, Dfg>, 'defaultVisitingOrder' | 'defaultVisitingType'>;
11
+ /**
12
+ * The control flow graph visitor to infer the shape of data frames using abstract interpretation
13
+ */
14
+ export declare class DataFrameShapeInferenceVisitor<OtherInfo = NoInfo, ControlFlow extends ControlFlowInformation = ControlFlowInformation, Ast extends NormalizedAst<OtherInfo & AbstractInterpretationInfo> = NormalizedAst<OtherInfo & AbstractInterpretationInfo>, Dfg extends DataflowGraph = DataflowGraph, Config extends DataFrameShapeInferenceVisitorConfiguration<OtherInfo, ControlFlow, Ast, Dfg> = DataFrameShapeInferenceVisitorConfiguration<OtherInfo, ControlFlow, Ast, Dfg>> extends SemanticCfgGuidedVisitor<OtherInfo & AbstractInterpretationInfo, ControlFlow, Ast, Dfg, Config & {
15
+ defaultVisitingOrder: 'forward';
16
+ defaultVisitingType: 'exit';
17
+ }> {
18
+ /**
19
+ * The old domain of an AST node before processing the node retrieved from the attached {@link AbstractInterpretationInfo}.
20
+ * This is used to check whether the state has changed and successors should be visited again, and is also required for widening.
21
+ */
22
+ private oldDomain;
23
+ /**
24
+ * The new domain of an AST node during and after processing the node.
25
+ * This information is stored in the {@link AbstractInterpretationInfo} afterwards.
26
+ */
27
+ private newDomain;
28
+ constructor(config: Config);
29
+ protected visitNode(nodeId: NodeId): boolean;
30
+ protected visitDataflowNode(vertex: Exclude<CfgSimpleVertex, CfgBasicBlockVertex>): void;
31
+ protected onVariableDefinition({ vertex }: {
32
+ vertex: DataflowGraphVertexVariableDefinition;
33
+ }): void;
34
+ protected onAssignmentCall({ call, target, source }: {
35
+ call: DataflowGraphVertexFunctionCall;
36
+ target?: NodeId;
37
+ source?: NodeId;
38
+ }): void;
39
+ protected onAccessCall({ call }: {
40
+ call: DataflowGraphVertexFunctionCall;
41
+ }): void;
42
+ protected onDefaultFunctionCall({ call }: {
43
+ call: DataflowGraphVertexFunctionCall;
44
+ }): void;
45
+ protected onReplacementCall({ call, source, target }: {
46
+ call: DataflowGraphVertexFunctionCall;
47
+ source: NodeId | undefined;
48
+ target: NodeId | undefined;
49
+ }): void;
50
+ private processOperation;
51
+ private processDataFrameAssignment;
52
+ private processDataFrameExpression;
53
+ /** We only process vertices of leaf nodes and exit vertices (no entry nodes of complex nodes) */
54
+ private shouldSkipVertex;
55
+ /** Get all AST nodes for the predecessor vertices that are leaf nodes and exit vertices */
56
+ private getPredecessorNodes;
57
+ private shouldWiden;
58
+ private clearUnassignedInfo;
59
+ }
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DataFrameShapeInferenceVisitor = 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 assert_1 = require("../../util/assert");
7
+ const absint_info_1 = require("./absint-info");
8
+ const domain_1 = require("./domain");
9
+ const access_mapper_1 = require("./mappers/access-mapper");
10
+ const assignment_mapper_1 = require("./mappers/assignment-mapper");
11
+ const function_mapper_1 = require("./mappers/function-mapper");
12
+ const replacement_mapper_1 = require("./mappers/replacement-mapper");
13
+ const semantics_1 = require("./semantics");
14
+ const shape_inference_1 = require("./shape-inference");
15
+ /**
16
+ * The control flow graph visitor to infer the shape of data frames using abstract interpretation
17
+ */
18
+ class DataFrameShapeInferenceVisitor extends semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor {
19
+ /**
20
+ * The old domain of an AST node before processing the node retrieved from the attached {@link AbstractInterpretationInfo}.
21
+ * This is used to check whether the state has changed and successors should be visited again, and is also required for widening.
22
+ */
23
+ oldDomain = new Map();
24
+ /**
25
+ * The new domain of an AST node during and after processing the node.
26
+ * This information is stored in the {@link AbstractInterpretationInfo} afterwards.
27
+ */
28
+ newDomain = new Map();
29
+ constructor(config) {
30
+ super({ ...config, defaultVisitingOrder: 'forward', defaultVisitingType: 'exit' });
31
+ }
32
+ visitNode(nodeId) {
33
+ const vertex = this.getCfgVertex(nodeId);
34
+ // skip vertices representing mid markers or entries of complex nodes
35
+ if (vertex === undefined || this.shouldSkipVertex(vertex)) {
36
+ return true;
37
+ }
38
+ const predecessors = this.getPredecessorNodes(vertex.id);
39
+ this.newDomain = (0, domain_1.joinDataFrameStates)(...predecessors.map(node => node.info.dataFrame?.domain ?? new Map()));
40
+ this.onVisitNode(nodeId);
41
+ const visitedCount = this.visited.get(vertex.id) ?? 0;
42
+ this.visited.set(vertex.id, visitedCount + 1);
43
+ // only continue visitor if the node has not been visited before or the data frame value of the node changed
44
+ return visitedCount === 0 || !(0, domain_1.equalDataFrameState)(this.oldDomain, this.newDomain);
45
+ }
46
+ visitDataflowNode(vertex) {
47
+ const node = this.getNormalizedAst((0, control_flow_graph_1.getVertexRootId)(vertex));
48
+ if (node === undefined) {
49
+ return;
50
+ }
51
+ this.oldDomain = node.info.dataFrame?.domain ?? new Map();
52
+ super.visitDataflowNode(vertex);
53
+ if (this.shouldWiden(vertex)) {
54
+ this.newDomain = (0, domain_1.wideningDataFrameStates)(this.oldDomain, this.newDomain);
55
+ }
56
+ node.info.dataFrame ??= {};
57
+ node.info.dataFrame.domain = this.newDomain;
58
+ }
59
+ onVariableDefinition({ vertex }) {
60
+ const node = this.getNormalizedAst(vertex.id);
61
+ if (node !== undefined) {
62
+ // mark variable definitions as "unassigned", as the evaluation of the assigned expression is delayed until processing the assignment
63
+ node.info.dataFrame ??= { marker: absint_info_1.DataFrameInfoMarker.Unassigned };
64
+ }
65
+ }
66
+ onAssignmentCall({ call, target, source }) {
67
+ const node = this.getNormalizedAst(call.id);
68
+ const targetNode = this.getNormalizedAst(target);
69
+ const sourceNode = this.getNormalizedAst(source);
70
+ if (node !== undefined && (0, assignment_mapper_1.isAssignmentTarget)(targetNode) && sourceNode !== undefined) {
71
+ node.info.dataFrame = (0, assignment_mapper_1.mapDataFrameVariableAssignment)(targetNode, sourceNode, this.config.dfg);
72
+ this.processOperation(node);
73
+ this.clearUnassignedInfo(targetNode);
74
+ }
75
+ }
76
+ onAccessCall({ call }) {
77
+ const node = this.getNormalizedAst(call.id);
78
+ if (node !== undefined) {
79
+ node.info.dataFrame = (0, access_mapper_1.mapDataFrameAccess)(node, this.config.dfg);
80
+ this.processOperation(node);
81
+ }
82
+ }
83
+ onDefaultFunctionCall({ call }) {
84
+ const node = this.getNormalizedAst(call.id);
85
+ if (node !== undefined) {
86
+ node.info.dataFrame = (0, function_mapper_1.mapDataFrameFunctionCall)(node, this.config.dfg, this.config.flowrConfig);
87
+ this.processOperation(node);
88
+ }
89
+ }
90
+ onReplacementCall({ call, source, target }) {
91
+ const node = this.getNormalizedAst(call.id);
92
+ const targetNode = this.getNormalizedAst(target);
93
+ const sourceNode = this.getNormalizedAst(source);
94
+ if (node !== undefined && targetNode !== undefined && sourceNode !== undefined) {
95
+ node.info.dataFrame = (0, replacement_mapper_1.mapDataFrameReplacementFunction)(node, sourceNode, this.config.dfg);
96
+ this.processOperation(node);
97
+ this.clearUnassignedInfo(targetNode);
98
+ }
99
+ }
100
+ processOperation(node) {
101
+ if ((0, absint_info_1.hasDataFrameAssignmentInfo)(node)) {
102
+ this.processDataFrameAssignment(node);
103
+ }
104
+ else if ((0, absint_info_1.hasDataFrameExpressionInfo)(node)) {
105
+ this.processDataFrameExpression(node);
106
+ }
107
+ }
108
+ processDataFrameAssignment(node) {
109
+ const value = (0, shape_inference_1.resolveIdToDataFrameShape)(node.info.dataFrame.expression, this.config.dfg, this.newDomain);
110
+ if (value !== undefined) {
111
+ this.newDomain.set(node.info.dataFrame.identifier, value);
112
+ const identifier = this.getNormalizedAst(node.info.dataFrame.identifier);
113
+ if (identifier !== undefined) {
114
+ identifier.info.dataFrame ??= {};
115
+ identifier.info.dataFrame.domain = new Map(this.newDomain);
116
+ }
117
+ }
118
+ }
119
+ processDataFrameExpression(node) {
120
+ let value = domain_1.DataFrameTop;
121
+ for (const { operation, operand, type, options, ...args } of node.info.dataFrame.operations) {
122
+ const operandValue = operand !== undefined ? (0, shape_inference_1.resolveIdToDataFrameShape)(operand, this.config.dfg, this.newDomain) : value;
123
+ value = (0, semantics_1.applySemantics)(operation, operandValue ?? domain_1.DataFrameTop, args, options);
124
+ const constraintType = type ?? (0, semantics_1.getConstraintType)(operation);
125
+ if (operand !== undefined && constraintType === semantics_1.ConstraintType.OperandModification) {
126
+ this.newDomain.set(operand, value);
127
+ for (const origin of (0, shape_inference_1.getVariableOrigins)(operand, this.config.dfg)) {
128
+ this.newDomain.set(origin.info.id, value);
129
+ }
130
+ }
131
+ else if (constraintType === semantics_1.ConstraintType.ResultPostcondition) {
132
+ this.newDomain.set(node.info.id, value);
133
+ }
134
+ }
135
+ }
136
+ /** We only process vertices of leaf nodes and exit vertices (no entry nodes of complex nodes) */
137
+ shouldSkipVertex(vertex) {
138
+ return (0, control_flow_graph_1.isMarkerVertex)(vertex) ? vertex.type !== control_flow_graph_1.CfgVertexType.EndMarker : vertex.end !== undefined;
139
+ }
140
+ /** Get all AST nodes for the predecessor vertices that are leaf nodes and exit vertices */
141
+ getPredecessorNodes(vertexId) {
142
+ return this.config.controlFlow.graph.outgoingEdges(vertexId)?.keys() // outgoing dependency edges are incoming CFG edges
143
+ .map(id => this.getCfgVertex(id))
144
+ .flatMap(vertex => {
145
+ if (vertex === undefined) {
146
+ return [];
147
+ }
148
+ else if (this.shouldSkipVertex(vertex)) {
149
+ return this.getPredecessorNodes(vertex.id);
150
+ }
151
+ else {
152
+ return [this.getNormalizedAst((0, control_flow_graph_1.getVertexRootId)(vertex))];
153
+ }
154
+ })
155
+ .filter(assert_1.isNotUndefined)
156
+ .toArray() ?? [];
157
+ }
158
+ shouldWiden(vertex) {
159
+ return (this.visited.get(vertex.id) ?? 0) >= this.config.flowrConfig.abstractInterpretation.dataFrame.wideningThreshold;
160
+ }
161
+ clearUnassignedInfo(node) {
162
+ if ((0, absint_info_1.hasDataFrameInfoMarker)(node, absint_info_1.DataFrameInfoMarker.Unassigned)) {
163
+ if (node.info.dataFrame?.domain !== undefined) {
164
+ node.info.dataFrame = { domain: node.info.dataFrame.domain };
165
+ }
166
+ else {
167
+ delete node.info.dataFrame;
168
+ }
169
+ }
170
+ }
171
+ }
172
+ exports.DataFrameShapeInferenceVisitor = DataFrameShapeInferenceVisitor;
173
+ //# sourceMappingURL=absint-visitor.js.map
@@ -0,0 +1,107 @@
1
+ import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
2
+ type Interval = [number, number];
3
+ /** The bottom element (least element) of the positive interval domain representing no possible values, explicitly given as "bottom". */
4
+ export declare const IntervalBottom = "bottom";
5
+ /** The top element (greatest element) of the positive interval domain representing all possible values, defined as the interval from 0 to infinity. */
6
+ export declare const IntervalTop: [0, number];
7
+ /** The positive interval domain representing possible integer values. */
8
+ export type IntervalDomain = Interval | typeof IntervalBottom;
9
+ /** The bottom element (least element) of the column names domain representing no possible column name, defined as the empty list []. */
10
+ export declare const ColNamesBottom: [];
11
+ /** The top element (greatest element) of the column names domain representing all possible values, explicitly given as "top". */
12
+ export declare const ColNamesTop = "top";
13
+ /** The column names domain defined as bounded string set domain representing possible column names. */
14
+ export type ColNamesDomain = string[] | typeof ColNamesTop;
15
+ /**
16
+ * The data frame shape domain representing possible data frame shapes, defined as product domain of
17
+ * the {@link ColNamesDomain} for the columns names and {@link IntervalDomain} for the number of columns and rows.
18
+ * */
19
+ export interface DataFrameDomain {
20
+ colnames: ColNamesDomain;
21
+ cols: IntervalDomain;
22
+ rows: IntervalDomain;
23
+ }
24
+ /**
25
+ * The bottom element (least element) of the data frame shape domain representing no possible value, mapping the columns names to {@link ColNamesBottom}
26
+ * and the number of columns and rows to {@link IntervalBottom}.
27
+ */
28
+ export declare const DataFrameBottom: {
29
+ readonly colnames: [];
30
+ readonly cols: "bottom";
31
+ readonly rows: "bottom";
32
+ };
33
+ /**
34
+ * The top element (greatest element) of the data frame shape domain representing all possible value, mapping the columns names to {@link ColNamesTop}
35
+ * and the number of columns and rows to {@link IntervalTop}.
36
+ */
37
+ export declare const DataFrameTop: {
38
+ readonly colnames: "top";
39
+ readonly cols: [0, number];
40
+ readonly rows: [0, number];
41
+ };
42
+ /**
43
+ * The data frame shape state domain representing possible memory states, mapping AST node IDs to data frame shape values of the {@link DataFrameDomain}.
44
+ */
45
+ export type DataFrameStateDomain = Map<NodeId, DataFrameDomain>;
46
+ /** Checks if two abstract values of the column names domain are equal. */
47
+ export declare function equalColNames(set1: ColNamesDomain, set2: ColNamesDomain): boolean;
48
+ /** Checks if two abstract values of the column names domain are ordered according to the partial ordering of the column names lattice. */
49
+ export declare function leqColNames(set1: ColNamesDomain, set2: ColNamesDomain): boolean;
50
+ /** Joins two abstract values of the columns names domain according to the column names lattice by creating the least upper bound (LUB). */
51
+ export declare function joinColNames(set1: ColNamesDomain, set2: ColNamesDomain, maxColNames?: number): ColNamesDomain;
52
+ /** Meets two abstract values of the columns names domain according to the column names lattice by creating the greatest lower bound (GLB). */
53
+ export declare function meetColNames(set1: ColNamesDomain, set2: ColNamesDomain): ColNamesDomain;
54
+ /** Subtracts an abstract value from another abstract value of the column names domain by performing a set minus. */
55
+ export declare function subtractColNames(set1: ColNamesDomain, set2: ColNamesDomain): ColNamesDomain;
56
+ /**
57
+ * Widens two abstract values of the column names domain via naive widening to soundly over-approximate the join in (possibly infinite) fixpoint iterations.
58
+ *
59
+ * This is technically not necessary, as the join is limited by the maximum number of inferred column names.
60
+ * However, this speeds up the iteration in larger loops significantly, as we are over-approximating the column names much earlier.
61
+ */
62
+ export declare function wideningColNames(set1: ColNamesDomain, set2: ColNamesDomain): ColNamesDomain;
63
+ /** Checks whether an abstract value of the column names domain satisfies a column name */
64
+ export declare function satisfiesColsNames(set: ColNamesDomain, value: string): boolean;
65
+ /** Checks if two abstract values of the positive interval domain are equal. */
66
+ export declare function equalInterval(interval1: IntervalDomain, interval2: IntervalDomain): boolean;
67
+ /** Checks if two abstract values of the positive interval domain are ordered according to the partial ordering of the positive interval lattice. */
68
+ export declare function leqInterval(interval1: IntervalDomain, interval2: IntervalDomain): boolean;
69
+ /** Joins two abstract values of the positive interval domain according to the positive interval lattice by creating the least upper bound (LUB). */
70
+ export declare function joinInterval(interval1: IntervalDomain, interval2: IntervalDomain): IntervalDomain;
71
+ /** Meets two abstract values of the positive interval domain according to the positive interval lattice by creating the greatest lower bound (GLB). */
72
+ export declare function meetInterval(interval1: IntervalDomain, interval2: IntervalDomain): IntervalDomain;
73
+ /** Adds two abstract values of the positive interval domain, by adding the lower bounds and upper bounds. */
74
+ export declare function addInterval(interval1: IntervalDomain, interval2: IntervalDomain): IntervalDomain;
75
+ /** Subtracts an abstract value from another abstract values of the positive interval domain, by subtracting the lower bounds and upper bounds. */
76
+ export declare function subtractInterval(interval1: IntervalDomain, interval2: IntervalDomain): IntervalDomain;
77
+ /** Creates the minium of two abstract values of the positive interval domain, by creating the minimum of the lower bounds and upper bounds. */
78
+ export declare function minInterval(interval1: IntervalDomain, interval2: IntervalDomain): IntervalDomain;
79
+ /** Creates the maximum of two abstract values of the positive interval domain, by creating the maximum of the lower bounds and upper bounds. */
80
+ export declare function maxInterval(interval1: IntervalDomain, interval2: IntervalDomain): IntervalDomain;
81
+ /** Extrends the lower bound of an abstract value of the positive interval domain to 0. */
82
+ export declare function extendIntervalToZero(interval: IntervalDomain): IntervalDomain;
83
+ /** Extrends the upper bound of an abstract value of the positive interval domain to infinity. */
84
+ export declare function extendIntervalToInfinity(interval: IntervalDomain): IntervalDomain;
85
+ /** Widens two abstract values of the positive interval domain via naive widening to soundly over-approximate the join in (possibly infinite) fixpoint iterations. */
86
+ export declare function wideningInterval(interval1: IntervalDomain, interval2: IntervalDomain): IntervalDomain;
87
+ /** Checks whether an abstract value of the positive interval domain satisfies a numeric value. */
88
+ export declare function satisfiesInterval(interval: IntervalDomain, value: number): boolean;
89
+ /** Checks whether a numeric value satisfies the less-than relation with an abstract value of the positive interval domain. */
90
+ export declare function satisfiesLeqInterval(interval: IntervalDomain, value: number): boolean;
91
+ /** Checks if two abstract values of the data frame shape domain are equal. */
92
+ export declare function equalDataFrameDomain(value1: DataFrameDomain, value2: DataFrameDomain): boolean;
93
+ /** Joins multiple abstract values of the data frame shape domain by creating the least upper bound (LUB). */
94
+ export declare function joinDataFrames(...values: DataFrameDomain[]): DataFrameDomain;
95
+ /** Meets multiple abstract values of the data frame shape domain by creating the greatest lower bound (GLB). */
96
+ export declare function meetDataFrames(...values: DataFrameDomain[]): DataFrameDomain;
97
+ /** Widens two abstract values of the data frame shape domain by widening the column names and number of columns and rows. */
98
+ export declare function wideningDataFrames(value1: DataFrameDomain, value2: DataFrameDomain): DataFrameDomain;
99
+ /** Checks if two abstract states of the data frame shape state domain are equal. */
100
+ export declare function equalDataFrameState(state1: DataFrameStateDomain, state2: DataFrameStateDomain): boolean;
101
+ /** Joins multiple abstract states of the data frame shape state domain by joining each data frame shape of the states. */
102
+ export declare function joinDataFrameStates(...states: DataFrameStateDomain[]): DataFrameStateDomain;
103
+ /** Meets multiple abstract states of the data frame shape state domain by meeting each data frame shape of the states. */
104
+ export declare function meetDataFrameStates(...states: DataFrameStateDomain[]): DataFrameStateDomain;
105
+ /** Widens two abstract states of the data frame shape state domain by widening each data frame shape of the states. */
106
+ export declare function wideningDataFrameStates(state1: DataFrameStateDomain, state2: DataFrameStateDomain): DataFrameStateDomain;
107
+ export {};