@eagleoutice/flowr 2.8.12 → 2.8.13
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 +23 -22
- package/control-flow/simple-visitor.js +3 -1
- package/dataflow/environments/default-builtin-config.d.ts +1 -1
- package/dataflow/environments/default-builtin-config.js +1 -1
- package/dataflow/extractor.js +1 -1
- package/dataflow/graph/graph.d.ts +1 -14
- package/dataflow/graph/graph.js +1 -10
- package/dataflow/internal/linker.d.ts +12 -0
- package/dataflow/internal/linker.js +24 -11
- package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +5 -4
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +3 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +7 -4
- package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +16 -10
- package/dataflow/internal/process/functions/call/built-in/built-in-get.js +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.js +3 -2
- package/dataflow/internal/process/functions/process-argument.js +2 -1
- package/dataflow/internal/process/functions/process-parameter.js +4 -3
- package/documentation/wiki-query.js +2 -3
- package/package.json +1 -1
- package/queries/catalog/call-context-query/call-context-query-executor.d.ts +1 -1
- package/queries/catalog/call-context-query/call-context-query-executor.js +4 -3
- package/queries/catalog/call-context-query/call-context-query-format.d.ts +21 -1
- package/queries/catalog/call-context-query/call-context-query-format.js +15 -7
- package/queries/catalog/call-context-query/identify-link-to-last-call-relation.d.ts +11 -3
- package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +14 -1
- package/queries/catalog/call-context-query/identify-link-to-nested-call-relation.d.ts +12 -0
- package/queries/catalog/call-context-query/identify-link-to-nested-call-relation.js +30 -0
- package/queries/catalog/call-context-query/identify-link-to-relation.d.ts +10 -0
- package/queries/catalog/call-context-query/identify-link-to-relation.js +21 -0
- package/queries/catalog/dependencies-query/dependencies-query-format.js +1 -1
- package/queries/catalog/dependencies-query/function-info/test-functions.js +146 -12
- package/queries/query.js +1 -1
- package/search/search-executor/search-enrichers.d.ts +8 -40
- package/search/search-executor/search-enrichers.js +17 -13
- package/search/search-executor/search-generators.js +5 -7
- package/search/search-executor/search-transformer.js +1 -7
- package/util/schema.js +6 -8
- package/util/version.js +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ It offers a wide variety of features, for example:
|
|
|
24
24
|
|
|
25
25
|
```shell
|
|
26
26
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
27
|
-
flowR repl using flowR v2.8.
|
|
27
|
+
flowR repl using flowR v2.8.12, R grammar v14 (tree-sitter engine)
|
|
28
28
|
R> :query @linter "read.csv(\"/root/x.txt\")"
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -33,13 +33,13 @@ It offers a wide variety of features, for example:
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
```text
|
|
36
|
-
Query: linter (
|
|
36
|
+
Query: linter (1 ms)
|
|
37
37
|
╰ Deprecated Functions (deprecated-functions):
|
|
38
|
-
╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs:
|
|
38
|
+
╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0
|
|
39
39
|
╰ File Path Validity (file-path-validity):
|
|
40
40
|
╰ certain:
|
|
41
41
|
╰ Path `/root/x.txt` at 1.1-23
|
|
42
|
-
╰ Metadata: totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs:
|
|
42
|
+
╰ Metadata: totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 1, processTimeMs: 0
|
|
43
43
|
╰ Seeded Randomness (seeded-randomness):
|
|
44
44
|
╰ Metadata: consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 0, processTimeMs: 0
|
|
45
45
|
╰ Absolute Paths (absolute-file-paths):
|
|
@@ -53,12 +53,12 @@ It offers a wide variety of features, for example:
|
|
|
53
53
|
╰ Network Functions (network-functions):
|
|
54
54
|
╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0
|
|
55
55
|
╰ Dataframe Access Validation (dataframe-access-validation):
|
|
56
|
-
╰ Metadata: numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs:
|
|
56
|
+
╰ Metadata: numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 0
|
|
57
57
|
╰ Dead Code (dead-code):
|
|
58
58
|
╰ Metadata: consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0
|
|
59
59
|
╰ Useless Loops (useless-loop):
|
|
60
60
|
╰ Metadata: numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0
|
|
61
|
-
All queries together required ≈
|
|
61
|
+
All queries together required ≈1 ms (1ms accuracy, total 1 ms)
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
|
|
@@ -80,13 +80,13 @@ It offers a wide variety of features, for example:
|
|
|
80
80
|
|
|
81
81
|
_Results (prettified and summarized):_
|
|
82
82
|
|
|
83
|
-
Query: **linter** (
|
|
83
|
+
Query: **linter** (1 ms)\
|
|
84
84
|
╰ **Deprecated Functions** (deprecated-functions):\
|
|
85
|
-
╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs:
|
|
85
|
+
╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
86
86
|
╰ **File Path Validity** (file-path-validity):\
|
|
87
87
|
╰ certain:\
|
|
88
88
|
╰ Path `/root/x.txt` at 1.1-23\
|
|
89
|
-
╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs:
|
|
89
|
+
╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 1</code>\
|
|
90
90
|
╰ **Seeded Randomness** (seeded-randomness):\
|
|
91
91
|
╰ _Metadata_: <code>consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
92
92
|
╰ **Absolute Paths** (absolute-file-paths):\
|
|
@@ -98,18 +98,18 @@ It offers a wide variety of features, for example:
|
|
|
98
98
|
╰ **Naming Convention** (naming-convention):\
|
|
99
99
|
╰ _Metadata_: <code>numMatches: 0, numBreak: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
100
100
|
╰ **Network Functions** (network-functions):\
|
|
101
|
-
╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs:
|
|
101
|
+
╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
102
102
|
╰ **Dataframe Access Validation** (dataframe-access-validation):\
|
|
103
103
|
╰ _Metadata_: <code>numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
104
104
|
╰ **Dead Code** (dead-code):\
|
|
105
105
|
╰ _Metadata_: <code>consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
106
106
|
╰ **Useless Loops** (useless-loop):\
|
|
107
107
|
╰ _Metadata_: <code>numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0</code>\
|
|
108
|
-
_All queries together required ≈
|
|
108
|
+
_All queries together required ≈1 ms (1ms accuracy, total 2 ms)_
|
|
109
109
|
|
|
110
110
|
<details> <summary style="color:gray">Show Detailed Results as Json</summary>
|
|
111
111
|
|
|
112
|
-
The analysis required
|
|
112
|
+
The analysis required _1.9 ms_ (including parsing and normalization and the query) within the generation environment.
|
|
113
113
|
|
|
114
114
|
In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR.
|
|
115
115
|
Please consult the [Interface](https://github.com/flowr-analysis/flowr/wiki/Interface) wiki page for more information on how to get those.
|
|
@@ -126,7 +126,7 @@ It offers a wide variety of features, for example:
|
|
|
126
126
|
".meta": {
|
|
127
127
|
"totalCalls": 0,
|
|
128
128
|
"totalFunctionDefinitions": 0,
|
|
129
|
-
"searchTimeMs":
|
|
129
|
+
"searchTimeMs": 0,
|
|
130
130
|
"processTimeMs": 0
|
|
131
131
|
}
|
|
132
132
|
},
|
|
@@ -150,7 +150,7 @@ It offers a wide variety of features, for example:
|
|
|
150
150
|
"totalWritesBeforeAlways": 0,
|
|
151
151
|
"totalValid": 0,
|
|
152
152
|
"searchTimeMs": 0,
|
|
153
|
-
"processTimeMs":
|
|
153
|
+
"processTimeMs": 1
|
|
154
154
|
}
|
|
155
155
|
},
|
|
156
156
|
"seeded-randomness": {
|
|
@@ -207,7 +207,7 @@ It offers a wide variety of features, for example:
|
|
|
207
207
|
".meta": {
|
|
208
208
|
"totalCalls": 0,
|
|
209
209
|
"totalFunctionDefinitions": 0,
|
|
210
|
-
"searchTimeMs":
|
|
210
|
+
"searchTimeMs": 0,
|
|
211
211
|
"processTimeMs": 0
|
|
212
212
|
}
|
|
213
213
|
},
|
|
@@ -239,11 +239,11 @@ It offers a wide variety of features, for example:
|
|
|
239
239
|
}
|
|
240
240
|
},
|
|
241
241
|
".meta": {
|
|
242
|
-
"timing":
|
|
242
|
+
"timing": 1
|
|
243
243
|
}
|
|
244
244
|
},
|
|
245
245
|
".meta": {
|
|
246
|
-
"timing":
|
|
246
|
+
"timing": 1
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
```
|
|
@@ -308,7 +308,7 @@ It offers a wide variety of features, for example:
|
|
|
308
308
|
|
|
309
309
|
```shell
|
|
310
310
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
311
|
-
flowR repl using flowR v2.8.
|
|
311
|
+
flowR repl using flowR v2.8.12, R grammar v14 (tree-sitter engine)
|
|
312
312
|
R> :query @static-slice (11@sum) file://test/testfiles/example.R
|
|
313
313
|
```
|
|
314
314
|
|
|
@@ -322,7 +322,7 @@ It offers a wide variety of features, for example:
|
|
|
322
322
|
N <- 10
|
|
323
323
|
for(i in 1:(N-1)) sum <- sum + i + w
|
|
324
324
|
sum
|
|
325
|
-
All queries together required ≈
|
|
325
|
+
All queries together required ≈2 ms (1ms accuracy, total 3 ms)
|
|
326
326
|
```
|
|
327
327
|
|
|
328
328
|
|
|
@@ -356,7 +356,7 @@ It offers a wide variety of features, for example:
|
|
|
356
356
|
|
|
357
357
|
|
|
358
358
|
* 🚀 **fast call-graph, data-, and control-flow graphs**\
|
|
359
|
-
Within just [<i><span title="This measurement is automatically fetched from the latest benchmark!">
|
|
359
|
+
Within just [<i><span title="This measurement is automatically fetched from the latest benchmark!">122.5 ms</span></i> (as of Jan 28, 2026)](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark),
|
|
360
360
|
_flowR_ can analyze the data- and control-flow of the average real-world R script. See the [benchmarks](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark) for more information,
|
|
361
361
|
and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/dataflow-graph) for more details on the dataflow graphs as well as call graphs.
|
|
362
362
|
|
|
@@ -392,7 +392,7 @@ It offers a wide variety of features, for example:
|
|
|
392
392
|
|
|
393
393
|
```shell
|
|
394
394
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
395
|
-
flowR repl using flowR v2.8.
|
|
395
|
+
flowR repl using flowR v2.8.12, R grammar v14 (tree-sitter engine)
|
|
396
396
|
R> :dataflow* test/testfiles/example.R
|
|
397
397
|
```
|
|
398
398
|
|
|
@@ -402,6 +402,7 @@ It offers a wide variety of features, for example:
|
|
|
402
402
|
|
|
403
403
|
```text
|
|
404
404
|
https://mermaid.live/view#base64:eyJjb2RlIjoiZmxvd2NoYXJ0IEJUXG4gICAgMChbXCJgIzkxO1JTeW1ib2wjOTM7IHRlc3RcbiAgICAgICgwKVxuICAgICAgKjEuMS00KmBcIl0pXG4gICAgMShbXCJgIzkxO1JTeW1ib2wjOTM7IHRlc3RmaWxlc1xuICAgICAgKDEpXG4gICAgICAqMS42LTE0KmBcIl0pXG4gICAgMltbXCJgIzkxO1JCaW5hcnlPcCM5MzsgL1xuICAgICAgKDIpXG4gICAgICAqMS4xLTE0KlxuICAgICgwLCAxKWBcIl1dXG4gICAgYnVpbHQtaW46X1tcImBCdWlsdC1Jbjpcbi9gXCJdXG4gICAgc3R5bGUgYnVpbHQtaW46XyBzdHJva2U6Z3JheSxmaWxsOmdyYXksc3Ryb2tlLXdpZHRoOjJweCxvcGFjaXR5Oi44O1xuICAgIDMoW1wiYCM5MTtSU3ltYm9sIzkzOyBleGFtcGxlLlJcbiAgICAgICgzKVxuICAgICAgKjEuMTYtMjQqYFwiXSlcbiAgICA0W1tcImAjOTE7UkJpbmFyeU9wIzkzOyAvXG4gICAgICAoNClcbiAgICAgICoxLjEtMjQqXG4gICAgKDIsIDMpYFwiXV1cbiAgICAyIC0tPnxcInJlYWRzLCBhcmd1bWVudFwifCAwXG4gICAgMiAtLT58XCJyZWFkcywgYXJndW1lbnRcInwgMVxuICAgIDIgLS4tPnxcInJlYWRzLCBjYWxsc1wifCBidWlsdC1pbjpfXG4gICAgbGlua1N0eWxlIDIgc3Ryb2tlOmdyYXk7XG4gICAgNCAtLT58XCJyZWFkcywgYXJndW1lbnRcInwgMlxuICAgIDQgLS0+fFwicmVhZHMsIGFyZ3VtZW50XCJ8IDNcbiAgICA0IC0uLT58XCJyZWFkcywgY2FsbHNcInwgYnVpbHQtaW46X1xuICAgIGxpbmtTdHlsZSA1IHN0cm9rZTpncmF5OyIsIm1lcm1haWQiOnsiYXV0b1N5bmMiOnRydWV9fQ==
|
|
405
|
+
Copied mermaid url to clipboard (dataflow: 0ms).
|
|
405
406
|
```
|
|
406
407
|
|
|
407
408
|
|
|
@@ -697,7 +698,7 @@ It offers a wide variety of features, for example:
|
|
|
697
698
|
```
|
|
698
699
|
|
|
699
700
|
|
|
700
|
-
(The analysis required
|
|
701
|
+
(The analysis required _1.2 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
|
|
701
702
|
|
|
702
703
|
|
|
703
704
|
|
|
@@ -293,7 +293,7 @@ export declare const DefaultBuiltinConfig: [{
|
|
|
293
293
|
readonly assumePrimitive: true;
|
|
294
294
|
}, {
|
|
295
295
|
readonly type: "function";
|
|
296
|
-
readonly names: ["stopifnot"];
|
|
296
|
+
readonly names: ["stopifnot", "assert_that"];
|
|
297
297
|
readonly processor: BuiltInProcName.StopIfNot;
|
|
298
298
|
readonly config: {};
|
|
299
299
|
readonly assumePrimitive: false;
|
|
@@ -242,7 +242,7 @@ exports.DefaultBuiltinConfig = [
|
|
|
242
242
|
},
|
|
243
243
|
{ type: 'function', names: ['try'], processor: built_in_1.BuiltInProcName.Try, config: { block: 'expr', handlers: {} }, assumePrimitive: true },
|
|
244
244
|
{ type: 'function', names: ['tryCatch', 'tryCatchLog'], processor: built_in_1.BuiltInProcName.Try, config: { block: 'expr', handlers: { error: 'error', finally: 'finally' } }, assumePrimitive: true },
|
|
245
|
-
{ type: 'function', names: ['stopifnot'], processor: built_in_1.BuiltInProcName.StopIfNot, config: {}, assumePrimitive: false },
|
|
245
|
+
{ type: 'function', names: ['stopifnot', 'assert_that'], processor: built_in_1.BuiltInProcName.StopIfNot, config: {}, assumePrimitive: false },
|
|
246
246
|
{ type: 'function', names: ['break'], processor: built_in_1.BuiltInProcName.Default, config: { useAsProcessor: built_in_1.BuiltInProcName.Break, cfg: 2 /* ExitPointType.Break */ }, assumePrimitive: false },
|
|
247
247
|
{ type: 'function', names: ['next'], processor: built_in_1.BuiltInProcName.Default, config: { cfg: 3 /* ExitPointType.Next */ }, assumePrimitive: false },
|
|
248
248
|
{ type: 'function', names: ['{'], processor: built_in_1.BuiltInProcName.ExpressionList, config: {}, assumePrimitive: true },
|
package/dataflow/extractor.js
CHANGED
|
@@ -89,7 +89,7 @@ function resolveLinkToSideEffects(ast, graph) {
|
|
|
89
89
|
continue;
|
|
90
90
|
}
|
|
91
91
|
/* this has to change whenever we add a new link to relations because we currently offer no abstraction for the type */
|
|
92
|
-
const potentials = (0, identify_link_to_last_call_relation_1.
|
|
92
|
+
const potentials = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelationSync)(s.id, cf.graph, graph, s.linkTo, knownCalls);
|
|
93
93
|
for (const pot of potentials) {
|
|
94
94
|
graph.addEdge(s.id, pot, edge_1.EdgeType.Reads);
|
|
95
95
|
}
|
|
@@ -177,20 +177,7 @@ export declare class DataflowGraph<Vertex extends DataflowGraphVertexInfo = Data
|
|
|
177
177
|
* @see DataflowGraphVertexArgument
|
|
178
178
|
*/
|
|
179
179
|
addVertex(vertex: DataflowGraphVertexArgument & Omit<Vertex, keyof DataflowGraphVertexArgument>, fallbackEnv: REnvironmentInformation, asRoot?: boolean, overwrite?: boolean): this;
|
|
180
|
-
|
|
181
|
-
addEdge(from: NodeId, to: NodeId, type: EdgeType | number): this;
|
|
182
|
-
/** {@inheritDoc} */
|
|
183
|
-
addEdge(from: {
|
|
184
|
-
nodeId: NodeId;
|
|
185
|
-
}, to: {
|
|
186
|
-
nodeId: NodeId;
|
|
187
|
-
}, type: EdgeType | number): this;
|
|
188
|
-
/** {@inheritDoc} */
|
|
189
|
-
addEdge(from: NodeId | {
|
|
190
|
-
nodeId: NodeId;
|
|
191
|
-
}, to: NodeId | {
|
|
192
|
-
nodeId: NodeId;
|
|
193
|
-
}, type: EdgeType | number): this;
|
|
180
|
+
addEdge(fromId: NodeId, toId: NodeId, type: EdgeType | number): this;
|
|
194
181
|
/**
|
|
195
182
|
* Merges the other graph into *this* one (in-place). The return value is only for convenience.
|
|
196
183
|
* @param otherGraph - The graph to merge into this one
|
package/dataflow/graph/graph.js
CHANGED
|
@@ -222,8 +222,7 @@ class DataflowGraph {
|
|
|
222
222
|
}
|
|
223
223
|
return this;
|
|
224
224
|
}
|
|
225
|
-
addEdge(
|
|
226
|
-
const [fromId, toId] = extractEdgeIds(from, to);
|
|
225
|
+
addEdge(fromId, toId, type) {
|
|
227
226
|
if (fromId === toId) {
|
|
228
227
|
return this;
|
|
229
228
|
}
|
|
@@ -390,14 +389,6 @@ function mergeNodeInfos(current, next) {
|
|
|
390
389
|
}
|
|
391
390
|
return current;
|
|
392
391
|
}
|
|
393
|
-
/**
|
|
394
|
-
* Returns the ids of the dataflow vertices referenced.
|
|
395
|
-
*/
|
|
396
|
-
function extractEdgeIds(from, to) {
|
|
397
|
-
const fromId = typeof from === 'object' ? from.nodeId : from;
|
|
398
|
-
const toId = typeof to === 'object' ? to.nodeId : to;
|
|
399
|
-
return [fromId, toId];
|
|
400
|
-
}
|
|
401
392
|
function envFromJson(json) {
|
|
402
393
|
const parent = json.parent ? envFromJson(json.parent) : undefined;
|
|
403
394
|
const memory = new Map();
|
|
@@ -63,6 +63,18 @@ export declare function linkFunctionCalls(graph: DataflowGraph, idMap: AstIdMap,
|
|
|
63
63
|
export declare function getAllFunctionCallTargets(call: NodeId, graph: DataflowGraph, environment?: REnvironmentInformation): NodeId[];
|
|
64
64
|
/**
|
|
65
65
|
* Finds all linked function definitions starting from the given set of read ids.
|
|
66
|
+
* This is a complicated function, please only call it if you know what you are doing.
|
|
67
|
+
* For example, if you are interested in the called functions of a function call, use {@link getAllFunctionCallTargets} instead.
|
|
68
|
+
* This function here expects you to handle the accessed objects yourself (e.g,. already resolve the first layer of reads/returns/calls/... or resolve the identifier by name)
|
|
69
|
+
* and then pass in the relevant read ids.
|
|
70
|
+
* @example
|
|
71
|
+
* Consider a scenario like this:
|
|
72
|
+
* ```R
|
|
73
|
+
* x <- function() 3
|
|
74
|
+
* x()
|
|
75
|
+
* ```
|
|
76
|
+
* To resolve the call `x` in the second line, use {@link getAllFunctionCallTargets}!
|
|
77
|
+
* To know what fdefs the definition of `x` in the first line links to, you can use {@link getAllLinkedFunctionDefinitions|this function}.
|
|
66
78
|
*/
|
|
67
79
|
export declare function getAllLinkedFunctionDefinitions(functionDefinitionReadIds: ReadonlySet<NodeId>, dataflowGraph: DataflowGraph): [Set<Required<DataflowGraphVertexFunctionDefinition>>, Set<BuiltIn>];
|
|
68
80
|
/**
|
|
@@ -230,10 +230,10 @@ function linkFunctionCallWithSingleTarget(graph, { subflow: fnSubflow, exitPoint
|
|
|
230
230
|
if (defs === undefined) {
|
|
231
231
|
continue;
|
|
232
232
|
}
|
|
233
|
-
for (const
|
|
234
|
-
if (!(0, built_in_1.isBuiltIn)(
|
|
235
|
-
graph.addEdge(ingoing,
|
|
236
|
-
graph.addEdge(id,
|
|
233
|
+
for (const { nodeId } of defs) {
|
|
234
|
+
if (!(0, built_in_1.isBuiltIn)(nodeId)) {
|
|
235
|
+
graph.addEdge(ingoing.nodeId, nodeId, edge_1.EdgeType.DefinedByOnCall);
|
|
236
|
+
graph.addEdge(id, nodeId, edge_1.EdgeType.DefinesOnCall);
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
}
|
|
@@ -340,6 +340,18 @@ function getAllFunctionCallTargets(call, graph, environment) {
|
|
|
340
340
|
const LinkedFnFollowBits = edge_1.EdgeType.Reads | edge_1.EdgeType.DefinedBy | edge_1.EdgeType.DefinedByOnCall;
|
|
341
341
|
/**
|
|
342
342
|
* Finds all linked function definitions starting from the given set of read ids.
|
|
343
|
+
* This is a complicated function, please only call it if you know what you are doing.
|
|
344
|
+
* For example, if you are interested in the called functions of a function call, use {@link getAllFunctionCallTargets} instead.
|
|
345
|
+
* This function here expects you to handle the accessed objects yourself (e.g,. already resolve the first layer of reads/returns/calls/... or resolve the identifier by name)
|
|
346
|
+
* and then pass in the relevant read ids.
|
|
347
|
+
* @example
|
|
348
|
+
* Consider a scenario like this:
|
|
349
|
+
* ```R
|
|
350
|
+
* x <- function() 3
|
|
351
|
+
* x()
|
|
352
|
+
* ```
|
|
353
|
+
* To resolve the call `x` in the second line, use {@link getAllFunctionCallTargets}!
|
|
354
|
+
* To know what fdefs the definition of `x` in the first line links to, you can use {@link getAllLinkedFunctionDefinitions|this function}.
|
|
343
355
|
*/
|
|
344
356
|
function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGraph) {
|
|
345
357
|
const result = new Set();
|
|
@@ -350,13 +362,14 @@ function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGrap
|
|
|
350
362
|
const potential = Array.from(functionDefinitionReadIds);
|
|
351
363
|
const visited = new Set();
|
|
352
364
|
while (potential.length !== 0) {
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
365
|
+
const cid = potential.pop();
|
|
366
|
+
// console.log(`visiting ${recoverName(cid, dataflowGraph.idMap)} ${cid}`);
|
|
367
|
+
visited.add(cid);
|
|
368
|
+
if ((0, built_in_1.isBuiltIn)(cid)) {
|
|
369
|
+
builtIns.add(cid);
|
|
357
370
|
continue;
|
|
358
371
|
}
|
|
359
|
-
const currentInfo = dataflowGraph.get(
|
|
372
|
+
const currentInfo = dataflowGraph.get(cid, true);
|
|
360
373
|
if (currentInfo === undefined) {
|
|
361
374
|
continue;
|
|
362
375
|
}
|
|
@@ -375,7 +388,7 @@ function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGrap
|
|
|
375
388
|
}
|
|
376
389
|
}
|
|
377
390
|
}
|
|
378
|
-
if (hasReturnEdge) {
|
|
391
|
+
if (vertex.tag === vertex_1.VertexType.FunctionCall || hasReturnEdge) {
|
|
379
392
|
continue;
|
|
380
393
|
}
|
|
381
394
|
for (const [target, { types }] of edges) {
|
|
@@ -409,7 +422,7 @@ function linkInputs(referencesToLinkAgainstEnvironment, environmentInformation,
|
|
|
409
422
|
let allBuiltIn = true;
|
|
410
423
|
for (const target of probableTarget) {
|
|
411
424
|
// we can stick with maybe even if readId.attribute is always
|
|
412
|
-
graph.addEdge(bodyInput, target, edge_1.EdgeType.Reads);
|
|
425
|
+
graph.addEdge(bodyInput.nodeId, target.nodeId, edge_1.EdgeType.Reads);
|
|
413
426
|
if (!(0, identifier_1.isReferenceType)(target.type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) {
|
|
414
427
|
allBuiltIn = false;
|
|
415
428
|
}
|
|
@@ -145,10 +145,11 @@ function processApply(name, args, rootId, data, config) {
|
|
|
145
145
|
}
|
|
146
146
|
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${ingoing.nodeId} in closure of function definition ${rootId}`);
|
|
147
147
|
let allBuiltIn = true;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
information.graph.addEdge(
|
|
151
|
-
|
|
148
|
+
const inId = ingoing.nodeId;
|
|
149
|
+
for (const { nodeId, type } of resolved) {
|
|
150
|
+
information.graph.addEdge(inId, nodeId, edge_1.EdgeType.Reads);
|
|
151
|
+
information.graph.addEdge(rootId, nodeId, edge_1.EdgeType.Reads); // because the def. is the anonymous call
|
|
152
|
+
if (!(0, identifier_1.isReferenceType)(type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) {
|
|
152
153
|
allBuiltIn = false;
|
|
153
154
|
}
|
|
154
155
|
}
|
|
@@ -323,12 +323,13 @@ function markAsAssignment(information, nodeToDefine, sourceIds, rootIdOfAssignme
|
|
|
323
323
|
}
|
|
324
324
|
information.environment = (0, define_1.define)(nodeToDefine, assignmentConfig?.superAssignment, information.environment, data.ctx.config);
|
|
325
325
|
information.graph.setDefinitionOfVertex(nodeToDefine);
|
|
326
|
+
const nid = nodeToDefine.nodeId;
|
|
326
327
|
if (!assignmentConfig?.quoteSource) {
|
|
327
328
|
for (const sourceId of sourceIds) {
|
|
328
|
-
information.graph.addEdge(
|
|
329
|
+
information.graph.addEdge(nid, sourceId, edge_1.EdgeType.DefinedBy);
|
|
329
330
|
}
|
|
330
331
|
}
|
|
331
|
-
information.graph.addEdge(
|
|
332
|
+
information.graph.addEdge(nid, rootIdOfAssignment, edge_1.EdgeType.DefinedBy);
|
|
332
333
|
// kinda dirty, but we have to remove existing read edges for the symbol, added by the child
|
|
333
334
|
const out = information.graph.outgoingEdges(nodeToDefine.nodeId);
|
|
334
335
|
for (const [id, edge] of (out ?? [])) {
|
|
@@ -39,11 +39,14 @@ function linkReadNameToWriteIfPossible(read, environments, listEnvironments, rem
|
|
|
39
39
|
if (probableTarget === undefined) {
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
|
+
const rid = read.nodeId;
|
|
42
43
|
for (const target of probableTarget) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
const tid = target.nodeId;
|
|
45
|
+
if ((read.type === identifier_1.ReferenceType.Function || read.type === identifier_1.ReferenceType.BuiltInFunction) && (0, built_in_1.isBuiltIn)(target.definedAt)) {
|
|
46
|
+
nextGraph.addEdge(rid, tid, edge_1.EdgeType.Reads | edge_1.EdgeType.Calls);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
nextGraph.addEdge(rid, tid, edge_1.EdgeType.Reads);
|
|
47
50
|
}
|
|
48
51
|
}
|
|
49
52
|
}
|
|
@@ -204,10 +204,11 @@ function updateNestedFunctionClosures(graph, outEnvironment, fnId) {
|
|
|
204
204
|
remainingIn.push(ingoing);
|
|
205
205
|
continue;
|
|
206
206
|
}
|
|
207
|
+
const inId = ingoing.nodeId;
|
|
207
208
|
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${id} in closure of function definition ${fnId}`);
|
|
208
209
|
let allBuiltIn = true;
|
|
209
210
|
for (const ref of resolved) {
|
|
210
|
-
graph.addEdge(
|
|
211
|
+
graph.addEdge(inId, ref.nodeId, edge_1.EdgeType.Reads);
|
|
211
212
|
if (!(0, identifier_1.isReferenceType)(ref.type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) {
|
|
212
213
|
allBuiltIn = false;
|
|
213
214
|
}
|
|
@@ -259,6 +260,9 @@ function updateNestedFunctionCalls(graph, outEnvironment) {
|
|
|
259
260
|
continue;
|
|
260
261
|
}
|
|
261
262
|
graph.addEdge(id, target, edge_1.EdgeType.Calls);
|
|
263
|
+
for (const exitPoint of targetVertex.exitPoints) {
|
|
264
|
+
graph.addEdge(id, exitPoint.nodeId, edge_1.EdgeType.Returns);
|
|
265
|
+
}
|
|
262
266
|
const ingoingRefs = targetVertex.subflow.in;
|
|
263
267
|
const remainingIn = [];
|
|
264
268
|
for (const ingoing of ingoingRefs) {
|
|
@@ -267,11 +271,12 @@ function updateNestedFunctionCalls(graph, outEnvironment) {
|
|
|
267
271
|
remainingIn.push(ingoing);
|
|
268
272
|
continue;
|
|
269
273
|
}
|
|
274
|
+
const inId = ingoing.nodeId;
|
|
270
275
|
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${id} in closure of function definition ${id}`);
|
|
271
|
-
for (const
|
|
272
|
-
if (!(0, built_in_1.isBuiltIn)(
|
|
273
|
-
graph.addEdge(
|
|
274
|
-
graph.addEdge(id,
|
|
276
|
+
for (const { nodeId } of resolved) {
|
|
277
|
+
if (!(0, built_in_1.isBuiltIn)(nodeId)) {
|
|
278
|
+
graph.addEdge(inId, nodeId, edge_1.EdgeType.DefinedByOnCall);
|
|
279
|
+
graph.addEdge(id, nodeId, edge_1.EdgeType.DefinesOnCall);
|
|
275
280
|
}
|
|
276
281
|
}
|
|
277
282
|
}
|
|
@@ -305,9 +310,10 @@ function findPromiseLinkagesForParameters(parameters, readInParameters, paramete
|
|
|
305
310
|
const remainingRead = [];
|
|
306
311
|
for (const read of readInParameters) {
|
|
307
312
|
const resolved = read.name ? (0, resolve_by_name_1.resolveByName)(read.name, parameterEnvs, read.type) : undefined;
|
|
313
|
+
const rid = read.nodeId;
|
|
308
314
|
if (resolved !== undefined) {
|
|
309
|
-
for (const
|
|
310
|
-
parameters.addEdge(
|
|
315
|
+
for (const { nodeId } of resolved) {
|
|
316
|
+
parameters.addEdge(rid, nodeId, edge_1.EdgeType.Reads);
|
|
311
317
|
}
|
|
312
318
|
continue;
|
|
313
319
|
}
|
|
@@ -320,11 +326,11 @@ function findPromiseLinkagesForParameters(parameters, readInParameters, paramete
|
|
|
320
326
|
continue;
|
|
321
327
|
}
|
|
322
328
|
if (writingOuts[0].cds === undefined) {
|
|
323
|
-
parameters.addEdge(
|
|
329
|
+
parameters.addEdge(rid, writingOuts[0].nodeId, edge_1.EdgeType.Reads);
|
|
324
330
|
continue;
|
|
325
331
|
}
|
|
326
|
-
for (const
|
|
327
|
-
parameters.addEdge(
|
|
332
|
+
for (const { nodeId } of writingOuts) {
|
|
333
|
+
parameters.addEdge(rid, nodeId, edge_1.EdgeType.Reads);
|
|
328
334
|
}
|
|
329
335
|
}
|
|
330
336
|
return remainingRead;
|
|
@@ -40,7 +40,7 @@ function processGet(name, args, rootId, data) {
|
|
|
40
40
|
const firstArg = processedArguments[0];
|
|
41
41
|
if (firstArg) {
|
|
42
42
|
// get 'reads' its first argument
|
|
43
|
-
information.graph.addEdge(rootId, firstArg.entryPoint, edge_1.EdgeType.Reads);
|
|
43
|
+
information.graph.addEdge(rootId, firstArg.entryPoint, edge_1.EdgeType.Returns | edge_1.EdgeType.Reads);
|
|
44
44
|
}
|
|
45
45
|
return information;
|
|
46
46
|
}
|
|
@@ -132,8 +132,9 @@ function promoteCallToFunction(call, arg, info, data) {
|
|
|
132
132
|
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${ingoing.nodeId} in closure of function definition ${call}`);
|
|
133
133
|
let allBuiltIn = true;
|
|
134
134
|
for (const ref of resolved) {
|
|
135
|
-
|
|
136
|
-
info.graph.addEdge(
|
|
135
|
+
const rid = ref.nodeId;
|
|
136
|
+
info.graph.addEdge(ingoing.nodeId, rid, edge_1.EdgeType.Reads);
|
|
137
|
+
info.graph.addEdge(call, rid, edge_1.EdgeType.Reads); // because the def. is the anonymous call
|
|
137
138
|
if (!(0, identifier_1.isReferenceType)(ref.type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) {
|
|
138
139
|
allBuiltIn = false;
|
|
139
140
|
}
|
|
@@ -14,9 +14,10 @@ const vertex_1 = require("../../../graph/vertex");
|
|
|
14
14
|
function linkReadsForArgument(root, ingoingRefs, graph) {
|
|
15
15
|
const allIdsBeforeArguments = new Set((0, collect_1.collectAllIds)(root, n => n.type === type_1.RType.Argument && n.info.id !== root.info.id));
|
|
16
16
|
const ingoingBeforeArgs = ingoingRefs.filter(r => allIdsBeforeArguments.has(r.nodeId));
|
|
17
|
+
const rid = root.info.id;
|
|
17
18
|
for (const ref of ingoingBeforeArgs) {
|
|
18
19
|
// link against the root reference currently I do not know how to deal with nested function calls otherwise
|
|
19
|
-
graph.addEdge(
|
|
20
|
+
graph.addEdge(rid, ref.nodeId, edge_1.EdgeType.Reads);
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
@@ -21,17 +21,18 @@ function processFunctionParameter(parameter, data) {
|
|
|
21
21
|
}));
|
|
22
22
|
let environment = name.environment;
|
|
23
23
|
for (const writtenNode of writtenNodes) {
|
|
24
|
-
|
|
24
|
+
const wid = writtenNode.nodeId;
|
|
25
|
+
(0, log_1.expensiveTrace)(log_1.log, () => `parameter ${writtenNode.name} (${wid}) is defined at id ${writtenNode.definedAt} with ${defaultValue === undefined ? 'no default value' : ' a default value'}`);
|
|
25
26
|
graph.setDefinitionOfVertex(writtenNode);
|
|
26
27
|
environment = (0, define_1.define)(writtenNode, false, environment, data.ctx.config);
|
|
27
28
|
if (defaultValue !== undefined) {
|
|
28
29
|
if (parameter.defaultValue?.type === type_1.RType.FunctionDefinition) {
|
|
29
|
-
graph.addEdge(
|
|
30
|
+
graph.addEdge(wid, parameter.defaultValue.info.id, edge_1.EdgeType.DefinedBy);
|
|
30
31
|
}
|
|
31
32
|
else {
|
|
32
33
|
const definedBy = defaultValue.in.concat(defaultValue.unknownReferences);
|
|
33
34
|
for (const node of definedBy) {
|
|
34
|
-
graph.addEdge(
|
|
35
|
+
graph.addEdge(wid, node.nodeId, edge_1.EdgeType.DefinedBy);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
}
|
|
@@ -57,8 +57,7 @@ Besides this, we provide the following ways to automatically categorize and link
|
|
|
57
57
|
|
|
58
58
|
1. **Kind** (\`kind\`): This is a general category that can be used to group calls together. For example, you may want to link all calls to \`plot\` to \`visualize\`.
|
|
59
59
|
2. **Subkind** (\`subkind\`): This is used to uniquely identify the respective call type when grouping the output. For example, you may want to link all calls to \`ggplot\` to \`plot\`.
|
|
60
|
-
3. **Linked Calls** (\`linkTo\`): This links the current call to the last call of the given kind. This way, you can link a call like \`points\` to the latest graphics plot etc.
|
|
61
|
-
For now, we _only_ offer support for linking to the last call, as the current flow dependency over-approximation is not stable.
|
|
60
|
+
3. **Linked Calls** (\`linkTo\`): This links the current call to the last/nested/.. call of the given kind. This way, you can link a call like \`points\` to the latest graphics plot etc.
|
|
62
61
|
4. **Aliases** (\`includeAliases\`): Consider a case like \`f <- function_of_interest\`, do you want calls to \`f\` to be included in the results? There is probably no need to combine this with a global call target!
|
|
63
62
|
|
|
64
63
|
It's also possible to filter the results based on the following properties:
|
|
@@ -804,7 +803,7 @@ ${(0, doc_query_1.tocForQueryType)('virtual')}
|
|
|
804
803
|
|
|
805
804
|
<summary>Detailed Query Format (Automatically Generated)</summary>
|
|
806
805
|
|
|
807
|
-
Although it is probably better to consult the detailed explanations
|
|
806
|
+
Although it is probably better to consult the detailed explanations, if you want to have a look at the schema, here is its description:
|
|
808
807
|
|
|
809
808
|
${(0, schema_1.describeSchema)((0, query_1.QueriesSchema)(), ansi_1.markdownFormatter)}
|
|
810
809
|
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import type { BasicQueryData } from '../../base-query-format';
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
export declare function promoteCallName(callName: CallNameTypes, exact?: boolean): RegExp | Set<string>;
|
|
7
|
-
export type PromotedLinkTo = Omit<
|
|
7
|
+
export type PromotedLinkTo<LT = LinkTo> = Omit<LT, 'callName'> & {
|
|
8
8
|
callName: RegExp | Set<string>;
|
|
9
9
|
};
|
|
10
10
|
/**
|
|
@@ -10,6 +10,7 @@ const objects_1 = require("../../../util/objects");
|
|
|
10
10
|
const identify_link_to_last_call_relation_1 = require("./identify-link-to-last-call-relation");
|
|
11
11
|
const cfg_kind_1 = require("../../../project/cfg-kind");
|
|
12
12
|
const extract_cfg_1 = require("../../../control-flow/extract-cfg");
|
|
13
|
+
const identify_link_to_relation_1 = require("./identify-link-to-relation");
|
|
13
14
|
/* if the node is effected by nse, we have an ingoing nse edge */
|
|
14
15
|
function isQuoted(node, graph) {
|
|
15
16
|
const vertex = graph.ingoingEdges(node);
|
|
@@ -239,10 +240,10 @@ async function executeCallContextQueries({ analyzer }, queries) {
|
|
|
239
240
|
const linked = Array.isArray(query.linkTo) ? query.linkTo : [query.linkTo];
|
|
240
241
|
for (const link of linked) {
|
|
241
242
|
/* if we have a linkTo query, we have to find the last call */
|
|
242
|
-
const
|
|
243
|
-
if (
|
|
243
|
+
const linkTos = await (0, identify_link_to_relation_1.identifyLinkToRelation)(nodeId, analyzer, link, calls);
|
|
244
|
+
if (linkTos) {
|
|
244
245
|
linkedIds ??= new Set();
|
|
245
|
-
for (const l of
|
|
246
|
+
for (const l of linkTos) {
|
|
246
247
|
if (link.attachLinkInfo) {
|
|
247
248
|
linkedIds.add({ id: l, info: link.attachLinkInfo });
|
|
248
249
|
}
|