@eagleoutice/flowr 2.7.6 → 2.8.1
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 +67 -64
- package/cli/wiki.js +1 -1
- package/control-flow/extract-cfg.js +3 -3
- package/control-flow/useless-loop.d.ts +1 -1
- package/control-flow/useless-loop.js +2 -2
- package/dataflow/cluster.js +3 -3
- package/dataflow/environments/built-in-config.d.ts +8 -4
- package/dataflow/environments/built-in.d.ts +27 -14
- package/dataflow/environments/built-in.js +27 -12
- package/dataflow/environments/default-builtin-config.d.ts +615 -3
- package/dataflow/environments/default-builtin-config.js +50 -15
- package/dataflow/environments/environment.js +3 -2
- package/dataflow/environments/identifier.d.ts +5 -1
- package/dataflow/environments/reference-to-maybe.d.ts +2 -2
- package/dataflow/environments/reference-to-maybe.js +23 -14
- package/dataflow/environments/resolve-by-name.d.ts +6 -2
- package/dataflow/environments/resolve-by-name.js +5 -1
- package/dataflow/environments/scoping.js +1 -3
- package/dataflow/eval/resolve/alias-tracking.js +5 -1
- package/dataflow/extractor.js +3 -3
- package/dataflow/fn/exceptions-of-function.d.ts +13 -0
- package/dataflow/fn/exceptions-of-function.js +47 -0
- package/dataflow/fn/higher-order-function.d.ts +1 -1
- package/dataflow/fn/higher-order-function.js +3 -3
- package/dataflow/fn/recursive-function.d.ts +6 -0
- package/dataflow/fn/recursive-function.js +32 -0
- package/dataflow/graph/call-graph.d.ts +10 -0
- package/dataflow/graph/call-graph.js +212 -0
- package/dataflow/graph/dataflowgraph-builder.d.ts +7 -2
- package/dataflow/graph/dataflowgraph-builder.js +14 -9
- package/dataflow/graph/diff-dataflow-graph.js +96 -2
- package/dataflow/graph/graph.d.ts +10 -7
- package/dataflow/graph/graph.js +7 -8
- package/dataflow/graph/vertex.d.ts +6 -3
- package/dataflow/hooks.d.ts +30 -0
- package/dataflow/hooks.js +38 -0
- package/dataflow/info.d.ts +28 -5
- package/dataflow/info.js +66 -31
- package/dataflow/internal/linker.d.ts +13 -3
- package/dataflow/internal/linker.js +163 -73
- package/dataflow/internal/process/functions/call/argument/unpack-argument.d.ts +4 -0
- package/dataflow/internal/process/functions/call/argument/unpack-argument.js +7 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-apply.d.ts +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +19 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +14 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +30 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-eval.js +2 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +24 -17
- package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +2 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.d.ts +5 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +59 -21
- package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +4 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-register-hook.d.ts +34 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-register-hook.js +92 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-repeat-loop.js +1 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-stop-if-not.d.ts +21 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-stop-if-not.js +129 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.d.ts +16 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.js +127 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +5 -3
- package/dataflow/internal/process/functions/call/common.d.ts +13 -1
- package/dataflow/internal/process/functions/call/common.js +33 -2
- package/dataflow/internal/process/functions/call/known-call-handling.d.ts +13 -1
- package/dataflow/internal/process/functions/call/known-call-handling.js +29 -3
- package/dataflow/internal/process/functions/call/named-call-handling.js +2 -1
- package/dataflow/internal/process/functions/call/unnamed-call-handling.js +6 -4
- package/dataflow/internal/process/functions/process-argument.js +7 -6
- package/dataflow/internal/process/functions/process-parameter.js +2 -1
- package/dataflow/internal/process/process-named-call.d.ts +2 -2
- package/dataflow/internal/process/process-symbol.js +3 -2
- package/dataflow/internal/process/process-value.d.ts +3 -2
- package/dataflow/internal/process/process-value.js +8 -6
- package/dataflow/origin/dfg-get-origin.js +2 -1
- package/dataflow/origin/dfg-get-symbol-refs.js +1 -1
- package/documentation/doc-readme.d.ts +1 -1
- package/documentation/doc-readme.js +6 -6
- package/documentation/doc-util/doc-code.js +1 -1
- package/documentation/doc-util/doc-dfg.d.ts +1 -0
- package/documentation/doc-util/doc-dfg.js +7 -4
- package/documentation/doc-util/doc-query.d.ts +1 -0
- package/documentation/doc-util/doc-query.js +1 -1
- package/documentation/doc-util/doc-repl.d.ts +2 -1
- package/documentation/doc-util/doc-repl.js +11 -3
- package/documentation/wiki-analyzer.js +2 -0
- package/documentation/wiki-dataflow-graph.js +59 -16
- package/documentation/wiki-interface.js +33 -5
- package/documentation/wiki-mk/doc-context.d.ts +2 -1
- package/documentation/wiki-mk/doc-context.js +2 -2
- package/documentation/wiki-mk/doc-maker.js +4 -3
- package/documentation/wiki-normalized-ast.js +6 -0
- package/documentation/wiki-query.js +109 -1
- package/linter/linter-rules.d.ts +1 -1
- package/linter/rules/seeded-randomness.js +17 -12
- package/linter/rules/useless-loop.d.ts +1 -1
- package/package.json +9 -9
- package/project/cache/flowr-analyzer-cache.d.ts +11 -0
- package/project/cache/flowr-analyzer-cache.js +19 -0
- package/project/context/flowr-analyzer-dependencies-context.d.ts +6 -1
- package/project/context/flowr-analyzer-dependencies-context.js +6 -0
- package/project/context/flowr-analyzer-files-context.d.ts +5 -2
- package/project/context/flowr-analyzer-files-context.js +24 -17
- package/project/context/flowr-file.d.ts +9 -4
- package/project/context/flowr-file.js +20 -6
- package/project/flowr-analyzer.d.ts +11 -0
- package/project/flowr-analyzer.js +6 -0
- package/project/plugins/file-plugins/files/flowr-description-file.d.ts +8 -0
- package/project/plugins/file-plugins/files/flowr-description-file.js +36 -3
- package/project/plugins/file-plugins/files/flowr-jupyter-file.js +1 -1
- package/project/plugins/file-plugins/files/flowr-namespace-file.js +1 -1
- package/project/plugins/file-plugins/files/flowr-news-file.js +1 -1
- package/project/plugins/file-plugins/files/flowr-rmarkdown-file.js +1 -1
- package/project/plugins/file-plugins/flowr-analyzer-description-file-plugin.js +1 -1
- package/project/plugins/file-plugins/flowr-analyzer-file-plugin.d.ts +4 -1
- package/project/plugins/file-plugins/flowr-analyzer-file-plugin.js +3 -0
- package/project/plugins/file-plugins/{flowr-analyzer-namespace-file-plugin.d.ts → flowr-analyzer-namespace-files-plugin.d.ts} +1 -1
- package/project/plugins/file-plugins/{flowr-analyzer-namespace-file-plugin.js → flowr-analyzer-namespace-files-plugin.js} +4 -4
- package/project/plugins/file-plugins/flowr-analyzer-test-file-plugin.d.ts +26 -0
- package/project/plugins/file-plugins/flowr-analyzer-test-file-plugin.js +39 -0
- package/project/plugins/file-plugins/flowr-analyzer-vignette-file-plugin.d.ts +26 -0
- package/project/plugins/file-plugins/flowr-analyzer-vignette-file-plugin.js +39 -0
- package/project/plugins/flowr-analyzer-plugin-defaults.js +6 -2
- package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-description-file-plugin.js +3 -13
- package/project/plugins/package-version-plugins/package.d.ts +1 -1
- package/project/plugins/package-version-plugins/package.js +3 -3
- package/project/plugins/plugin-registry.d.ts +4 -2
- package/project/plugins/plugin-registry.js +6 -2
- package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.d.ts +11 -0
- package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +5 -2
- package/queries/catalog/call-context-query/call-context-query-format.d.ts +4 -12
- package/queries/catalog/call-graph-query/call-graph-query-executor.d.ts +6 -0
- package/queries/catalog/call-graph-query/call-graph-query-executor.js +21 -0
- package/queries/catalog/call-graph-query/call-graph-query-format.d.ts +21 -0
- package/queries/catalog/call-graph-query/call-graph-query-format.js +32 -0
- package/queries/catalog/dataflow-query/dataflow-query-executor.js +4 -3
- package/queries/catalog/dependencies-query/dependencies-query-executor.js +29 -3
- package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +1 -0
- package/queries/catalog/dependencies-query/function-info/function-info.d.ts +8 -1
- package/queries/catalog/dependencies-query/function-info/write-functions.js +13 -0
- package/queries/catalog/does-call-query/does-call-query-executor.d.ts +6 -0
- package/queries/catalog/does-call-query/does-call-query-executor.js +100 -0
- package/queries/catalog/does-call-query/does-call-query-format.d.ts +51 -0
- package/queries/catalog/does-call-query/does-call-query-format.js +102 -0
- package/queries/catalog/files-query/files-query-executor.js +4 -4
- package/queries/catalog/files-query/files-query-format.d.ts +2 -1
- package/queries/catalog/files-query/files-query-format.js +18 -2
- package/queries/catalog/id-map-query/id-map-query-executor.js +4 -3
- package/queries/catalog/inspect-exceptions-query/inspect-exception-query-executor.d.ts +18 -0
- package/queries/catalog/inspect-exceptions-query/inspect-exception-query-executor.js +56 -0
- package/queries/catalog/inspect-exceptions-query/inspect-exception-query-format.d.ts +34 -0
- package/queries/catalog/inspect-exceptions-query/inspect-exception-query-format.js +54 -0
- package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.js +3 -3
- package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.d.ts +6 -0
- package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.js +12 -0
- package/queries/catalog/inspect-recursion-query/inspect-recursion-query-executor.d.ts +6 -0
- package/queries/catalog/inspect-recursion-query/inspect-recursion-query-executor.js +23 -0
- package/queries/catalog/inspect-recursion-query/inspect-recursion-query-format.d.ts +28 -0
- package/queries/catalog/inspect-recursion-query/inspect-recursion-query-format.js +44 -0
- package/queries/catalog/linter-query/linter-query-format.js +4 -1
- package/queries/catalog/location-map-query/location-map-query-executor.js +1 -1
- package/queries/catalog/normalized-ast-query/normalized-ast-query-executor.js +4 -3
- package/queries/catalog/project-query/project-query-executor.js +9 -3
- package/queries/catalog/project-query/project-query-format.d.ts +6 -1
- package/queries/catalog/project-query/project-query-format.js +35 -9
- package/queries/query.d.ts +34 -2
- package/queries/query.js +9 -0
- package/r-bridge/data/data.d.ts +10 -5
- package/r-bridge/data/data.js +11 -5
- package/r-bridge/lang-4.x/ast/model/model.d.ts +7 -7
- package/r-bridge/lang-4.x/ast/model/nodes/r-access.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-argument.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-binary-op.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-comment.d.ts +5 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-comment.js +8 -0
- package/r-bridge/lang-4.x/ast/model/nodes/r-expression-list.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-for-loop.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-function-call.d.ts +3 -3
- package/r-bridge/lang-4.x/ast/model/nodes/r-function-definition.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-if-then-else.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-parameter.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-pipe.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-repeat-loop.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-unary-op.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/nodes/r-while-loop.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/parser/main/internal/other/normalize-comment.js +0 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +0 -2
- package/r-bridge/roxygen2/roxygen-ast.d.ts +218 -0
- package/r-bridge/roxygen2/roxygen-ast.js +82 -0
- package/r-bridge/roxygen2/roxygen-parse.d.ts +24 -0
- package/r-bridge/roxygen2/roxygen-parse.js +214 -0
- package/reconstruct/auto-select/magic-comments.js +4 -4
- package/slicing/static/slice-call.js +3 -4
- package/slicing/static/static-slicer.js +2 -2
- package/statistics/features/supported/defined-functions/defined-functions.js +1 -1
- package/util/collections/defaultmap.d.ts +3 -3
- package/util/mermaid/dfg.js +5 -5
- package/util/objects.js +1 -1
- package/util/r-author.d.ts +5 -0
- package/util/r-author.js +110 -0
- package/util/r-license.d.ts +10 -1
- package/util/r-license.js +27 -6
- package/util/r-version.d.ts +19 -0
- package/util/r-version.js +106 -0
- package/util/range.d.ts +6 -0
- package/util/range.js +7 -0
- package/util/simple-df/dfg-ascii.js +2 -2
- package/util/text/args.d.ts +9 -0
- package/util/text/args.js +65 -0
- package/util/version.js +1 -1
package/dataflow/info.d.ts
CHANGED
|
@@ -4,11 +4,13 @@ import type { IdentifierReference } from './environments/identifier';
|
|
|
4
4
|
import type { REnvironmentInformation } from './environments/environment';
|
|
5
5
|
import { DataflowGraph } from './graph/graph';
|
|
6
6
|
import type { GenericDifferenceInformation, WriteableDifferenceReport } from '../util/diff';
|
|
7
|
+
import type { HookInformation } from './hooks';
|
|
7
8
|
/**
|
|
8
9
|
* A control dependency links a vertex to the control flow element which
|
|
9
10
|
* may have an influence on its execution.
|
|
10
11
|
* Within `if(p) a else b`, `a` and `b` have a control dependency on the `if` (which in turn decides based on `p`).
|
|
11
12
|
* @see {@link happensInEveryBranch} - to check whether a list of control dependencies is exhaustive
|
|
13
|
+
* @see {@link negateControlDependency} - to easily negate a control dependency
|
|
12
14
|
*/
|
|
13
15
|
export interface ControlDependency {
|
|
14
16
|
/** The id of the node that causes the control dependency to be active (e.g., the condition of an if) */
|
|
@@ -18,6 +20,11 @@ export interface ControlDependency {
|
|
|
18
20
|
/** whether this control dependency was created due to iteration (e.g., a loop) */
|
|
19
21
|
readonly byIteration?: boolean;
|
|
20
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Negates the given control dependency (i.e., flips the `when` flag).
|
|
25
|
+
* This keeps undefined `when` values intact as undefined.
|
|
26
|
+
*/
|
|
27
|
+
export declare function negateControlDependency(cd: ControlDependency): ControlDependency;
|
|
21
28
|
/**
|
|
22
29
|
* Classifies the type of exit point encountered.
|
|
23
30
|
* @see {@link ExitPoint}
|
|
@@ -30,8 +37,14 @@ export declare const enum ExitPointType {
|
|
|
30
37
|
/** The exit point is an explicit `break` call (or an alias of it) */
|
|
31
38
|
Break = 2,
|
|
32
39
|
/** The exit point is an explicit `next` call (or an alias of it) */
|
|
33
|
-
Next = 3
|
|
40
|
+
Next = 3,
|
|
41
|
+
/** The exit point is caused by an error being thrown, e.g., by `stop` or `stopifnot` */
|
|
42
|
+
Error = 4
|
|
34
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Checks whether the given exit point type propagates calls (i.e., whether it aborts the current function execution).
|
|
46
|
+
*/
|
|
47
|
+
export declare function doesExitPointPropagateCalls(type: ExitPointType): boolean;
|
|
35
48
|
/**
|
|
36
49
|
* An exit point describes the position which ends the current control flow structure.
|
|
37
50
|
* This may be as innocent as the last expression or explicit with a `return`/`break`/`next`.
|
|
@@ -50,18 +63,28 @@ export interface ExitPoint {
|
|
|
50
63
|
* (e.g., if the `return` is contained within an `if` statement).
|
|
51
64
|
* @see {@link happensInEveryBranch} - to check whether control dependencies are exhaustive
|
|
52
65
|
*/
|
|
53
|
-
readonly controlDependencies
|
|
66
|
+
readonly controlDependencies?: ControlDependency[] | undefined;
|
|
54
67
|
}
|
|
55
68
|
/**
|
|
56
|
-
* Adds all non-default exit points to the existing list.
|
|
69
|
+
* Adds all non-default exit points to the existing list and updates the `invertExitCds` accordingly.
|
|
57
70
|
*/
|
|
58
|
-
export declare function addNonDefaultExitPoints(existing: ExitPoint[], add: readonly ExitPoint[]): void;
|
|
71
|
+
export declare function addNonDefaultExitPoints(existing: ExitPoint[], invertExitCds: ControlDependency[], activeCds: ControlDependency[] | undefined, add: readonly ExitPoint[]): void;
|
|
72
|
+
/**
|
|
73
|
+
* Overwrites the existing exit points with the given ones, taking care of cds.
|
|
74
|
+
*/
|
|
75
|
+
export declare function overwriteExitPoints(existing: readonly ExitPoint[], replace: ExitPoint[]): ExitPoint[];
|
|
59
76
|
/** The control flow information for the current DataflowInformation. */
|
|
60
77
|
export interface DataflowCfgInformation {
|
|
61
78
|
/** The entry node into the subgraph */
|
|
62
79
|
entryPoint: NodeId;
|
|
63
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* All already identified exit points (active 'return'/'break'/'next'-likes) of the respective structure.
|
|
82
|
+
* This also tracks (local knowledge of) exceptions thrown within the structure.
|
|
83
|
+
* See the {@link ExitPointType#Error|Error} type for more information.
|
|
84
|
+
*/
|
|
64
85
|
exitPoints: readonly ExitPoint[];
|
|
86
|
+
/** Registered hooks within the current subtree */
|
|
87
|
+
hooks: HookInformation[];
|
|
65
88
|
}
|
|
66
89
|
/**
|
|
67
90
|
* The dataflow information is one of the fundamental structures we have in the dataflow analysis.
|
package/dataflow/info.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.negateControlDependency = negateControlDependency;
|
|
4
|
+
exports.doesExitPointPropagateCalls = doesExitPointPropagateCalls;
|
|
3
5
|
exports.addNonDefaultExitPoints = addNonDefaultExitPoints;
|
|
6
|
+
exports.overwriteExitPoints = overwriteExitPoints;
|
|
4
7
|
exports.initializeCleanDataflowInformation = initializeCleanDataflowInformation;
|
|
5
8
|
exports.happensInEveryBranch = happensInEveryBranch;
|
|
6
9
|
exports.happensInEveryBranchSet = happensInEveryBranchSet;
|
|
@@ -9,11 +12,48 @@ exports.filterOutLoopExitPoints = filterOutLoopExitPoints;
|
|
|
9
12
|
exports.diffControlDependency = diffControlDependency;
|
|
10
13
|
exports.diffControlDependencies = diffControlDependencies;
|
|
11
14
|
const graph_1 = require("./graph/graph");
|
|
15
|
+
const assert_1 = require("../util/assert");
|
|
12
16
|
/**
|
|
13
|
-
*
|
|
17
|
+
* Negates the given control dependency (i.e., flips the `when` flag).
|
|
18
|
+
* This keeps undefined `when` values intact as undefined.
|
|
14
19
|
*/
|
|
15
|
-
function
|
|
16
|
-
|
|
20
|
+
function negateControlDependency(cd) {
|
|
21
|
+
return {
|
|
22
|
+
...cd,
|
|
23
|
+
when: cd.when === undefined ? undefined : !cd.when,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Checks whether the given exit point type propagates calls (i.e., whether it aborts the current function execution).
|
|
28
|
+
*/
|
|
29
|
+
function doesExitPointPropagateCalls(type) {
|
|
30
|
+
return type === 4 /* ExitPointType.Error */;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Adds all non-default exit points to the existing list and updates the `invertExitCds` accordingly.
|
|
34
|
+
*/
|
|
35
|
+
function addNonDefaultExitPoints(existing, invertExitCds, activeCds, add) {
|
|
36
|
+
const toAdd = add.filter(({ type }) => type !== 0 /* ExitPointType.Default */);
|
|
37
|
+
if (toAdd.length === 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const invertedCds = toAdd.flatMap(e => e.controlDependencies?.filter(icd => !activeCds?.some(e => e.id === icd.id && e.when === icd.when)).map(negateControlDependency)).filter(assert_1.isNotUndefined);
|
|
41
|
+
existing.push(...toAdd);
|
|
42
|
+
for (const icd of invertedCds) {
|
|
43
|
+
if (!invertExitCds.some(e => e.id === icd.id && e.when === icd.when)) {
|
|
44
|
+
invertExitCds.push(icd);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Overwrites the existing exit points with the given ones, taking care of cds.
|
|
50
|
+
*/
|
|
51
|
+
function overwriteExitPoints(existing, replace) {
|
|
52
|
+
const replaceCds = replace.flatMap(e => e.controlDependencies);
|
|
53
|
+
if (replaceCds.length === 0 || replaceCds.some(r => r === undefined) || happensInEveryBranch(replaceCds.filter(e => e !== undefined))) {
|
|
54
|
+
return replace;
|
|
55
|
+
}
|
|
56
|
+
return existing.concat(replace);
|
|
17
57
|
}
|
|
18
58
|
/**
|
|
19
59
|
* Initializes an empty {@link DataflowInformation} object with the given entry point and data.
|
|
@@ -28,7 +68,8 @@ function initializeCleanDataflowInformation(entryPoint, data) {
|
|
|
28
68
|
environment: data.environment,
|
|
29
69
|
graph: new graph_1.DataflowGraph(data.completeAst.idMap),
|
|
30
70
|
entryPoint,
|
|
31
|
-
exitPoints: [{ nodeId: entryPoint, type: 0 /* ExitPointType.Default
|
|
71
|
+
exitPoints: [{ nodeId: entryPoint, type: 0 /* ExitPointType.Default */ }],
|
|
72
|
+
hooks: []
|
|
32
73
|
};
|
|
33
74
|
}
|
|
34
75
|
/**
|
|
@@ -37,28 +78,21 @@ function initializeCleanDataflowInformation(entryPoint, data) {
|
|
|
37
78
|
* @see {@link happensInEveryBranchSet} - for the set-based version
|
|
38
79
|
*/
|
|
39
80
|
function happensInEveryBranch(controlDependencies) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
else if (controlDependencies.length === 0) {
|
|
45
|
-
/* this happens only when we have no idea and require more analysis */
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
return coversSet(controlDependencies);
|
|
81
|
+
/* this happens only when we have no idea and require more analysis */
|
|
82
|
+
return controlDependencies === undefined || (controlDependencies.length !== 0 && coversSet(controlDependencies));
|
|
49
83
|
}
|
|
50
84
|
function coversSet(controlDependencies) {
|
|
51
|
-
const trues =
|
|
52
|
-
const
|
|
85
|
+
const trues = new Set();
|
|
86
|
+
const falses = new Set();
|
|
53
87
|
for (const { id, when } of controlDependencies) {
|
|
54
88
|
if (when) {
|
|
55
|
-
trues.
|
|
89
|
+
trues.add(id);
|
|
56
90
|
}
|
|
57
|
-
else {
|
|
58
|
-
|
|
91
|
+
else if (when === false) {
|
|
92
|
+
falses.add(id);
|
|
59
93
|
}
|
|
60
94
|
}
|
|
61
|
-
return trues.
|
|
95
|
+
return trues.symmetricDifference(falses).size === 0;
|
|
62
96
|
}
|
|
63
97
|
/**
|
|
64
98
|
* Checks whether the given control dependencies are exhaustive (i.e. if for every control dependency on a boolean,
|
|
@@ -66,28 +100,29 @@ function coversSet(controlDependencies) {
|
|
|
66
100
|
* @see {@link happensInEveryBranch} - for the array-based version
|
|
67
101
|
*/
|
|
68
102
|
function happensInEveryBranchSet(controlDependencies) {
|
|
69
|
-
|
|
70
|
-
/* the cds are unconstrained */
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
else if (controlDependencies.size === 0) {
|
|
74
|
-
/* this happens only when we have no idea and require more analysis */
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
return coversSet(controlDependencies);
|
|
103
|
+
return controlDependencies === undefined || (controlDependencies.size !== 0 && coversSet(controlDependencies));
|
|
78
104
|
}
|
|
79
105
|
/**
|
|
80
106
|
* Checks whether the given dataflow information always exits (i.e., if there is a non-default exit point in every branch).
|
|
81
107
|
* @see {@link ExitPoint} - for the different types of exit points
|
|
82
108
|
*/
|
|
83
109
|
function alwaysExits(data) {
|
|
84
|
-
|
|
110
|
+
let cds = [];
|
|
111
|
+
for (const e of data.exitPoints) {
|
|
112
|
+
if (e.type !== 0 /* ExitPointType.Default */) {
|
|
113
|
+
if (e.controlDependencies === undefined) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
cds = cds.concat(e.controlDependencies);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return happensInEveryBranch(cds);
|
|
85
120
|
}
|
|
86
121
|
/**
|
|
87
122
|
* Filters out exit points which end their cascade within a loop.
|
|
88
123
|
*/
|
|
89
124
|
function filterOutLoopExitPoints(exitPoints) {
|
|
90
|
-
return exitPoints.filter(({ type }) => type
|
|
125
|
+
return exitPoints.filter(({ type }) => type !== 2 /* ExitPointType.Break */ && type !== 3 /* ExitPointType.Next */);
|
|
91
126
|
}
|
|
92
127
|
/**
|
|
93
128
|
* Calculates the difference between two control dependencies.
|
|
@@ -100,7 +135,7 @@ function diffControlDependency(a, b, info) {
|
|
|
100
135
|
return;
|
|
101
136
|
}
|
|
102
137
|
if (a.id !== b.id) {
|
|
103
|
-
info.report.addComment(`${info.position}Different control dependency ids. ${info.leftname}: ${a.id} vs. ${info.rightname}: ${b.id}`);
|
|
138
|
+
info.report.addComment(`${info.position}Different control dependency ids. ${info.leftname}: ${JSON.stringify(a.id)} vs. ${info.rightname}: ${JSON.stringify(b.id)}`);
|
|
104
139
|
}
|
|
105
140
|
if (a.when !== b.when) {
|
|
106
141
|
info.report.addComment(`${info.position}Different control dependency when. ${info.leftname}: ${a.when} vs. ${info.rightname}: ${b.when}`);
|
|
@@ -21,12 +21,21 @@ export declare function produceNameSharedIdMap(references: IdentifierReference[]
|
|
|
21
21
|
* Links the given arguments to the given parameters within the given graph.
|
|
22
22
|
* This follows the `pmatch` semantics of R
|
|
23
23
|
* @see https://cran.r-project.org/doc/manuals/R-lang.html#Argument-matching
|
|
24
|
+
* This returns the resolved map from argument ids to parameter ids.
|
|
25
|
+
* If you just want to match by name, use {@link pMatch}.
|
|
24
26
|
*/
|
|
25
|
-
export declare function linkArgumentsOnCall(args: FunctionArgument[], params: RParameter<ParentInformation>[], graph: DataflowGraph):
|
|
27
|
+
export declare function linkArgumentsOnCall(args: readonly FunctionArgument[], params: readonly RParameter<ParentInformation>[], graph: DataflowGraph): Map<NodeId, NodeId>;
|
|
28
|
+
/**
|
|
29
|
+
* Links the given arguments to the given parameters within the given graph by name only.
|
|
30
|
+
* @note
|
|
31
|
+
* To obtain the arguments from a {@link RFunctionCall}[], either use {@link processAllArguments} (also available via {@link processKnownFunctionCall})
|
|
32
|
+
* or convert them with {@link convertFnArguments}.
|
|
33
|
+
*/
|
|
34
|
+
export declare function pMatch<Targets extends NodeId>(args: readonly FunctionArgument[], params: Record<string, Targets>): Map<NodeId, Targets>;
|
|
26
35
|
/**
|
|
27
36
|
* Links a function call with a single target function definition.
|
|
28
37
|
*/
|
|
29
|
-
export declare function linkFunctionCallWithSingleTarget(graph: DataflowGraph,
|
|
38
|
+
export declare function linkFunctionCallWithSingleTarget(graph: DataflowGraph, { subflow: fnSubflow, exitPoints, id: fnId, params }: DataflowGraphVertexFunctionDefinition, info: DataflowGraphVertexFunctionCall, idMap: AstIdMap): ExitPoint[];
|
|
30
39
|
/**
|
|
31
40
|
* Returns the called functions within the current graph, which can be used to merge the environments with the call.
|
|
32
41
|
* Furthermore, it links the corresponding arguments.
|
|
@@ -37,6 +46,7 @@ export declare function linkFunctionCallWithSingleTarget(graph: DataflowGraph, d
|
|
|
37
46
|
export declare function linkFunctionCalls(graph: DataflowGraph, idMap: AstIdMap, thisGraph: DataflowGraph): {
|
|
38
47
|
functionCall: NodeId;
|
|
39
48
|
called: readonly DataflowGraphVertexInfo[];
|
|
49
|
+
propagateExitPoints: readonly ExitPoint[];
|
|
40
50
|
}[];
|
|
41
51
|
/**
|
|
42
52
|
* convenience function returning all known call targets, as well as the name source which defines them
|
|
@@ -45,7 +55,7 @@ export declare function getAllFunctionCallTargets(call: NodeId, graph: DataflowG
|
|
|
45
55
|
/**
|
|
46
56
|
* Finds all linked function definitions starting from the given set of read ids.
|
|
47
57
|
*/
|
|
48
|
-
export declare function getAllLinkedFunctionDefinitions(functionDefinitionReadIds: ReadonlySet<NodeId>, dataflowGraph: DataflowGraph): [Set<
|
|
58
|
+
export declare function getAllLinkedFunctionDefinitions(functionDefinitionReadIds: ReadonlySet<NodeId>, dataflowGraph: DataflowGraph): [Set<Required<DataflowGraphVertexFunctionDefinition>>, Set<BuiltIn>];
|
|
49
59
|
/**
|
|
50
60
|
* This method links a set of read variables to definitions in an environment.
|
|
51
61
|
* @param referencesToLinkAgainstEnvironment - The set of references to link against the environment
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.findNonLocalReads = findNonLocalReads;
|
|
4
4
|
exports.produceNameSharedIdMap = produceNameSharedIdMap;
|
|
5
5
|
exports.linkArgumentsOnCall = linkArgumentsOnCall;
|
|
6
|
+
exports.pMatch = pMatch;
|
|
6
7
|
exports.linkFunctionCallWithSingleTarget = linkFunctionCallWithSingleTarget;
|
|
7
8
|
exports.linkFunctionCalls = linkFunctionCalls;
|
|
8
9
|
exports.getAllFunctionCallTargets = getAllFunctionCallTargets;
|
|
@@ -24,6 +25,7 @@ const vertex_1 = require("../graph/vertex");
|
|
|
24
25
|
const resolve_by_name_1 = require("../environments/resolve-by-name");
|
|
25
26
|
const built_in_1 = require("../environments/built-in");
|
|
26
27
|
const prefix_1 = require("../../util/prefix");
|
|
28
|
+
const info_1 = require("../info");
|
|
27
29
|
/**
|
|
28
30
|
* Find all reads within the graph that do not reference a local definition in the graph.
|
|
29
31
|
*/
|
|
@@ -32,33 +34,21 @@ function findNonLocalReads(graph, ignore) {
|
|
|
32
34
|
const ids = new Set(graph.vertexIdsOfType(vertex_1.VertexType.Use).concat(graph.vertexIdsOfType(vertex_1.VertexType.FunctionCall)));
|
|
33
35
|
/* find all variable use ids which do not link to a given id */
|
|
34
36
|
const nonLocalReads = [];
|
|
35
|
-
for (const
|
|
36
|
-
if (ignores.has(
|
|
37
|
+
for (const nodeId of ids) {
|
|
38
|
+
if (ignores.has(nodeId)) {
|
|
37
39
|
continue;
|
|
38
40
|
}
|
|
39
|
-
const outgoing = graph.outgoingEdges(
|
|
40
|
-
const name = (0, node_id_1.recoverName)(
|
|
41
|
-
const origin = graph.getVertex(
|
|
41
|
+
const outgoing = graph.outgoingEdges(nodeId);
|
|
42
|
+
const name = (0, node_id_1.recoverName)(nodeId, graph.idMap);
|
|
43
|
+
const origin = graph.getVertex(nodeId, true);
|
|
44
|
+
const type = origin?.tag === vertex_1.VertexType.FunctionCall ? identifier_1.ReferenceType.Function : identifier_1.ReferenceType.Variable;
|
|
42
45
|
if (outgoing === undefined) {
|
|
43
|
-
nonLocalReads.push({
|
|
44
|
-
name: (0, node_id_1.recoverName)(id, graph.idMap),
|
|
45
|
-
nodeId: id,
|
|
46
|
-
controlDependencies: undefined,
|
|
47
|
-
type: origin?.tag === vertex_1.VertexType.FunctionCall ? identifier_1.ReferenceType.Function : identifier_1.ReferenceType.Variable
|
|
48
|
-
});
|
|
46
|
+
nonLocalReads.push({ name, nodeId, type });
|
|
49
47
|
continue;
|
|
50
48
|
}
|
|
51
49
|
for (const [target, { types }] of outgoing) {
|
|
52
50
|
if ((0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.Reads) && !ids.has(target)) {
|
|
53
|
-
|
|
54
|
-
logger_1.dataflowLogger.warn('found non-local read without name for id ' + id);
|
|
55
|
-
}
|
|
56
|
-
nonLocalReads.push({
|
|
57
|
-
name: (0, node_id_1.recoverName)(id, graph.idMap),
|
|
58
|
-
nodeId: id,
|
|
59
|
-
controlDependencies: undefined,
|
|
60
|
-
type: origin?.tag === vertex_1.VertexType.FunctionCall ? identifier_1.ReferenceType.Function : identifier_1.ReferenceType.Variable
|
|
61
|
-
});
|
|
51
|
+
nonLocalReads.push({ name, nodeId, type });
|
|
62
52
|
break;
|
|
63
53
|
}
|
|
64
54
|
}
|
|
@@ -81,26 +71,34 @@ function produceNameSharedIdMap(references) {
|
|
|
81
71
|
* Links the given arguments to the given parameters within the given graph.
|
|
82
72
|
* This follows the `pmatch` semantics of R
|
|
83
73
|
* @see https://cran.r-project.org/doc/manuals/R-lang.html#Argument-matching
|
|
74
|
+
* This returns the resolved map from argument ids to parameter ids.
|
|
75
|
+
* If you just want to match by name, use {@link pMatch}.
|
|
84
76
|
*/
|
|
85
77
|
function linkArgumentsOnCall(args, params, graph) {
|
|
86
78
|
const nameArgMap = new Map(args.filter(graph_1.isNamedArgument).map(a => [a.name, a]));
|
|
87
79
|
const nameParamMap = new Map(params.filter(p => p?.name?.content !== undefined)
|
|
88
80
|
.map(p => [p.name.content, p]));
|
|
81
|
+
const maps = new Map();
|
|
89
82
|
const specialDotParameter = params.find(p => p.special);
|
|
83
|
+
const sid = specialDotParameter?.name.info.id;
|
|
90
84
|
// all parameters matched by name
|
|
91
85
|
const matchedParameters = new Set();
|
|
86
|
+
const paramNames = nameParamMap.keys().toArray();
|
|
92
87
|
// first map names
|
|
93
88
|
for (const [name, { nodeId: argId }] of nameArgMap) {
|
|
94
|
-
const pmatchName = (0, prefix_1.findByPrefixIfUnique)(name,
|
|
89
|
+
const pmatchName = (0, prefix_1.findByPrefixIfUnique)(name, paramNames) ?? name;
|
|
95
90
|
const param = nameParamMap.get(pmatchName);
|
|
96
91
|
if (param?.name) {
|
|
97
|
-
|
|
98
|
-
graph.addEdge(
|
|
92
|
+
const pid = param.name.info.id;
|
|
93
|
+
graph.addEdge(argId, pid, edge_1.EdgeType.DefinesOnCall);
|
|
94
|
+
graph.addEdge(pid, argId, edge_1.EdgeType.DefinedByOnCall);
|
|
95
|
+
maps.set(argId, pid);
|
|
99
96
|
matchedParameters.add(name);
|
|
100
97
|
}
|
|
101
|
-
else if (
|
|
102
|
-
graph.addEdge(argId,
|
|
103
|
-
graph.addEdge(
|
|
98
|
+
else if (sid) {
|
|
99
|
+
graph.addEdge(argId, sid, edge_1.EdgeType.DefinesOnCall);
|
|
100
|
+
graph.addEdge(sid, argId, edge_1.EdgeType.DefinedByOnCall);
|
|
101
|
+
maps.set(argId, sid);
|
|
104
102
|
}
|
|
105
103
|
}
|
|
106
104
|
const remainingParameter = params.filter(p => !p?.name || !matchedParameters.has(p.name.content));
|
|
@@ -110,10 +108,12 @@ function linkArgumentsOnCall(args, params, graph) {
|
|
|
110
108
|
if (arg === r_function_call_1.EmptyArgument) {
|
|
111
109
|
continue;
|
|
112
110
|
}
|
|
111
|
+
const aid = arg.nodeId;
|
|
113
112
|
if (remainingParameter.length <= i) {
|
|
114
|
-
if (
|
|
115
|
-
graph.addEdge(
|
|
116
|
-
graph.addEdge(
|
|
113
|
+
if (sid) {
|
|
114
|
+
graph.addEdge(aid, sid, edge_1.EdgeType.DefinesOnCall);
|
|
115
|
+
graph.addEdge(sid, aid, edge_1.EdgeType.DefinedByOnCall);
|
|
116
|
+
maps.set(aid, sid);
|
|
117
117
|
}
|
|
118
118
|
else {
|
|
119
119
|
logger_1.dataflowLogger.warn(`skipping argument ${i} as there is no corresponding parameter - R should block that`);
|
|
@@ -121,13 +121,64 @@ function linkArgumentsOnCall(args, params, graph) {
|
|
|
121
121
|
continue;
|
|
122
122
|
}
|
|
123
123
|
const param = remainingParameter[i];
|
|
124
|
-
logger_1.dataflowLogger.trace(`mapping unnamed argument ${i} (id: ${
|
|
124
|
+
logger_1.dataflowLogger.trace(`mapping unnamed argument ${i} (id: ${aid}) to parameter "${param.name?.content ?? '??'}"`);
|
|
125
125
|
if (param.name) {
|
|
126
|
-
|
|
127
|
-
graph.addEdge(
|
|
126
|
+
const pid = param.name.info.id;
|
|
127
|
+
graph.addEdge(aid, pid, edge_1.EdgeType.DefinesOnCall);
|
|
128
|
+
graph.addEdge(pid, aid, edge_1.EdgeType.DefinedByOnCall);
|
|
129
|
+
maps.set(aid, pid);
|
|
128
130
|
}
|
|
129
131
|
}
|
|
132
|
+
return maps;
|
|
130
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Links the given arguments to the given parameters within the given graph by name only.
|
|
136
|
+
* @note
|
|
137
|
+
* To obtain the arguments from a {@link RFunctionCall}[], either use {@link processAllArguments} (also available via {@link processKnownFunctionCall})
|
|
138
|
+
* or convert them with {@link convertFnArguments}.
|
|
139
|
+
*/
|
|
140
|
+
function pMatch(args, params) {
|
|
141
|
+
const nameArgMap = new Map(args.filter(graph_1.isNamedArgument).map(a => [a.name, a]));
|
|
142
|
+
const maps = new Map();
|
|
143
|
+
const sid = params['...'];
|
|
144
|
+
const paramNames = Object.keys(params);
|
|
145
|
+
// all parameters matched by name
|
|
146
|
+
const matchedParameters = new Set();
|
|
147
|
+
// first map names
|
|
148
|
+
for (const [name, { nodeId: argId }] of nameArgMap) {
|
|
149
|
+
const pmatchName = (0, prefix_1.findByPrefixIfUnique)(name, paramNames) ?? name;
|
|
150
|
+
const param = params[pmatchName];
|
|
151
|
+
if (param) {
|
|
152
|
+
maps.set(argId, param);
|
|
153
|
+
}
|
|
154
|
+
else if (sid) {
|
|
155
|
+
maps.set(argId, sid);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const remainingParameter = paramNames.filter(p => !matchedParameters.has(p));
|
|
159
|
+
const remainingArguments = args.filter(a => !(0, graph_1.isNamedArgument)(a));
|
|
160
|
+
for (let i = 0; i < remainingArguments.length; i++) {
|
|
161
|
+
const arg = remainingArguments[i];
|
|
162
|
+
if (arg === r_function_call_1.EmptyArgument) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const aid = arg.nodeId;
|
|
166
|
+
if (remainingParameter.length <= i) {
|
|
167
|
+
if (sid) {
|
|
168
|
+
maps.set(aid, sid);
|
|
169
|
+
}
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const param = params[remainingParameter[i]];
|
|
173
|
+
if (param) {
|
|
174
|
+
maps.set(aid, param);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return maps;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Links the function call arguments to the target function definition and returns a map from argument ids to parameter ids.
|
|
181
|
+
*/
|
|
131
182
|
function linkFunctionCallArguments(targetId, idMap, functionCallName, functionRootId, callArgs, finalGraph) {
|
|
132
183
|
// we get them by just choosing the rhs of the definition
|
|
133
184
|
const linkedFunction = idMap.get(targetId);
|
|
@@ -139,16 +190,16 @@ function linkFunctionCallArguments(targetId, idMap, functionCallName, functionRo
|
|
|
139
190
|
logger_1.dataflowLogger.trace(`function call definition base ${functionCallName} does not lead to a function definition (${functionRootId}) but got ${linkedFunction.type}`);
|
|
140
191
|
return;
|
|
141
192
|
}
|
|
142
|
-
linkArgumentsOnCall(callArgs, linkedFunction.parameters, finalGraph);
|
|
193
|
+
return linkArgumentsOnCall(callArgs, linkedFunction.parameters, finalGraph);
|
|
143
194
|
}
|
|
144
195
|
/**
|
|
145
196
|
* Links a function call with a single target function definition.
|
|
146
197
|
*/
|
|
147
|
-
function linkFunctionCallWithSingleTarget(graph,
|
|
198
|
+
function linkFunctionCallWithSingleTarget(graph, { subflow: fnSubflow, exitPoints, id: fnId, params }, info, idMap) {
|
|
148
199
|
const id = info.id;
|
|
149
200
|
if (info.environment !== undefined) {
|
|
150
201
|
// for each open ingoing reference, try to resolve it here, and if so, add a read edge from the call to signal that it reads it
|
|
151
|
-
for (const ingoing of
|
|
202
|
+
for (const ingoing of fnSubflow.in) {
|
|
152
203
|
const defs = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, info.environment, ingoing.type) : undefined;
|
|
153
204
|
if (defs === undefined) {
|
|
154
205
|
continue;
|
|
@@ -161,15 +212,32 @@ function linkFunctionCallWithSingleTarget(graph, def, info, idMap) {
|
|
|
161
212
|
}
|
|
162
213
|
}
|
|
163
214
|
}
|
|
164
|
-
const
|
|
215
|
+
const propagateExitPoints = [];
|
|
165
216
|
for (const exitPoint of exitPoints) {
|
|
166
|
-
graph.addEdge(id, exitPoint, edge_1.EdgeType.Returns);
|
|
217
|
+
graph.addEdge(id, exitPoint.nodeId, edge_1.EdgeType.Returns);
|
|
218
|
+
if ((0, info_1.doesExitPointPropagateCalls)(exitPoint.type)) {
|
|
219
|
+
// add the exit point to the call!
|
|
220
|
+
propagateExitPoints.push(exitPoint);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const defName = (0, node_id_1.recoverName)(fnId, idMap);
|
|
224
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `recording expr-list-level call from ${(0, node_id_1.recoverName)(info.id, idMap)} to ${defName}`);
|
|
225
|
+
graph.addEdge(id, fnId, edge_1.EdgeType.Calls);
|
|
226
|
+
applyForForcedArgs(graph, info.id, params, linkFunctionCallArguments(fnId, idMap, defName, id, info.args, graph));
|
|
227
|
+
return propagateExitPoints;
|
|
228
|
+
}
|
|
229
|
+
/** for each parameter that we link that gets forced, add a reads edge from the call to argument to show that it reads it */
|
|
230
|
+
function applyForForcedArgs(graph, callId, readParams, maps) {
|
|
231
|
+
if (maps === undefined) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
for (const [arg, param] of maps.entries()) {
|
|
235
|
+
if (readParams[String(param)]) {
|
|
236
|
+
graph.addEdge(callId, arg, edge_1.EdgeType.Reads);
|
|
237
|
+
}
|
|
167
238
|
}
|
|
168
|
-
const defName = (0, node_id_1.recoverName)(def.id, idMap);
|
|
169
|
-
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `recording expression-list-level call from ${(0, node_id_1.recoverName)(info.id, idMap)} to ${defName}`);
|
|
170
|
-
graph.addEdge(id, def.id, edge_1.EdgeType.Calls);
|
|
171
|
-
linkFunctionCallArguments(def.id, idMap, defName, id, info.args, graph);
|
|
172
239
|
}
|
|
240
|
+
const FCallLinkReadBits = edge_1.EdgeType.Reads | edge_1.EdgeType.Calls | edge_1.EdgeType.DefinedByOnCall;
|
|
173
241
|
/* there is _a lot_ potential for optimization here */
|
|
174
242
|
function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefinitions) {
|
|
175
243
|
const edges = graph.outgoingEdges(id);
|
|
@@ -177,16 +245,26 @@ function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefin
|
|
|
177
245
|
/* no outgoing edges */
|
|
178
246
|
return;
|
|
179
247
|
}
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
&& (0, edge_1.
|
|
183
|
-
|
|
248
|
+
const functionDefinitionReadIds = new Set();
|
|
249
|
+
for (const [t, { types }] of edges.entries()) {
|
|
250
|
+
if (!(0, built_in_1.isBuiltIn)(t) && (0, edge_1.edgeDoesNotIncludeType)(types, edge_1.EdgeType.Argument) && (0, edge_1.edgeIncludesType)(types, FCallLinkReadBits)) {
|
|
251
|
+
functionDefinitionReadIds.add(t);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const [functionDefs] = getAllLinkedFunctionDefinitions(new Set(functionDefinitionReadIds), graph);
|
|
255
|
+
const propagateExitPoints = [];
|
|
184
256
|
for (const def of functionDefs.values()) {
|
|
185
|
-
|
|
186
|
-
|
|
257
|
+
// we can skip this if we already linked it
|
|
258
|
+
const oEdge = graph.outgoingEdges(id)?.get(def.id);
|
|
259
|
+
if (oEdge && (0, edge_1.edgeIncludesType)(oEdge.types, edge_1.EdgeType.Calls)) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
for (const ep of linkFunctionCallWithSingleTarget(graph, def, info, idMap)) {
|
|
263
|
+
propagateExitPoints.push(ep);
|
|
264
|
+
}
|
|
187
265
|
}
|
|
188
266
|
if (thisGraph.isRoot(id) && functionDefs.size > 0) {
|
|
189
|
-
calledFunctionDefinitions.push({ functionCall: id, called:
|
|
267
|
+
calledFunctionDefinitions.push({ functionCall: id, called: functionDefs.values().toArray(), propagateExitPoints });
|
|
190
268
|
}
|
|
191
269
|
}
|
|
192
270
|
/**
|
|
@@ -199,7 +277,9 @@ function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefin
|
|
|
199
277
|
function linkFunctionCalls(graph, idMap, thisGraph) {
|
|
200
278
|
const calledFunctionDefinitions = [];
|
|
201
279
|
for (const [id, info] of thisGraph.verticesOfType(vertex_1.VertexType.FunctionCall)) {
|
|
202
|
-
|
|
280
|
+
if (!info.onlyBuiltin) {
|
|
281
|
+
linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefinitions);
|
|
282
|
+
}
|
|
203
283
|
}
|
|
204
284
|
return calledFunctionDefinitions;
|
|
205
285
|
}
|
|
@@ -227,21 +307,25 @@ function getAllFunctionCallTargets(call, graph, environment) {
|
|
|
227
307
|
for (const target of functionCallTargets) {
|
|
228
308
|
found.push(target.id);
|
|
229
309
|
}
|
|
230
|
-
found = found.concat(
|
|
310
|
+
found = found.concat(Array.from(builtInTargets), functionCallDefs);
|
|
231
311
|
}
|
|
232
312
|
return found;
|
|
233
313
|
}
|
|
314
|
+
const LinkedFnFollowBits = edge_1.EdgeType.Reads | edge_1.EdgeType.DefinedBy | edge_1.EdgeType.DefinedByOnCall;
|
|
234
315
|
/**
|
|
235
316
|
* Finds all linked function definitions starting from the given set of read ids.
|
|
236
317
|
*/
|
|
237
318
|
function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGraph) {
|
|
238
|
-
let potential = functionDefinitionReadIds.values().toArray();
|
|
239
|
-
const visited = new Set();
|
|
240
319
|
const result = new Set();
|
|
241
320
|
const builtIns = new Set();
|
|
242
|
-
|
|
321
|
+
if (functionDefinitionReadIds.size === 0) {
|
|
322
|
+
return [result, builtIns];
|
|
323
|
+
}
|
|
324
|
+
const potential = Array.from(functionDefinitionReadIds);
|
|
325
|
+
const visited = new Set();
|
|
326
|
+
while (potential.length !== 0) {
|
|
243
327
|
const currentId = potential.pop();
|
|
244
|
-
|
|
328
|
+
visited.add(currentId);
|
|
245
329
|
if ((0, built_in_1.isBuiltIn)(currentId)) {
|
|
246
330
|
builtIns.add(currentId);
|
|
247
331
|
continue;
|
|
@@ -250,24 +334,29 @@ function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGrap
|
|
|
250
334
|
if (currentInfo === undefined) {
|
|
251
335
|
continue;
|
|
252
336
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
.
|
|
257
|
-
if (returnEdges.length > 0) {
|
|
258
|
-
// only traverse return edges and do not follow `calls` etc. as this indicates that we have a function call which returns a result, and not the function calls itself
|
|
259
|
-
potential = potential.concat(returnEdges.map(([target]) => target).filter(id => !visited.has(id)));
|
|
337
|
+
const [vertex, edges] = currentInfo;
|
|
338
|
+
// Found a function definition
|
|
339
|
+
if (vertex.subflow !== undefined) {
|
|
340
|
+
result.add(vertex);
|
|
260
341
|
continue;
|
|
261
342
|
}
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
343
|
+
let hasReturnEdge = false;
|
|
344
|
+
for (const [target, { types }] of edges) {
|
|
345
|
+
if ((0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.Returns)) {
|
|
346
|
+
hasReturnEdge = true;
|
|
347
|
+
if (!visited.has(target)) {
|
|
348
|
+
potential.push(target);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (hasReturnEdge) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
for (const [target, { types }] of edges) {
|
|
356
|
+
if ((0, edge_1.edgeIncludesType)(types, LinkedFnFollowBits) && !visited.has(target)) {
|
|
357
|
+
potential.push(target);
|
|
358
|
+
}
|
|
266
359
|
}
|
|
267
|
-
// trace all joined reads
|
|
268
|
-
potential = potential.concat(followEdges
|
|
269
|
-
.map(([target]) => target)
|
|
270
|
-
.filter(id => !visited.has(id)));
|
|
271
360
|
}
|
|
272
361
|
return [result, builtIns];
|
|
273
362
|
}
|
|
@@ -327,10 +416,10 @@ function linkCircularRedefinitionsWithinALoop(graph, openIns, outgoing) {
|
|
|
327
416
|
}
|
|
328
417
|
}
|
|
329
418
|
for (const [name, targets] of openIns.entries()) {
|
|
330
|
-
for (const
|
|
331
|
-
if (
|
|
419
|
+
for (const { name: outName, nodeId } of lastOutgoing.values()) {
|
|
420
|
+
if (outName === name) {
|
|
332
421
|
for (const target of targets) {
|
|
333
|
-
graph.addEdge(target.nodeId,
|
|
422
|
+
graph.addEdge(target.nodeId, nodeId, edge_1.EdgeType.Reads);
|
|
334
423
|
}
|
|
335
424
|
}
|
|
336
425
|
}
|
|
@@ -344,8 +433,9 @@ function reapplyLoopExitPoints(exits, references) {
|
|
|
344
433
|
const exitCds = new Set(exits.flatMap(e => e.controlDependencies).filter(assert_1.isNotUndefined));
|
|
345
434
|
for (const ref of references) {
|
|
346
435
|
for (const cd of exitCds) {
|
|
436
|
+
const { id: cId, when: cWhen } = cd;
|
|
347
437
|
if (ref.controlDependencies) {
|
|
348
|
-
if (!ref.controlDependencies?.find(c => c.id ===
|
|
438
|
+
if (!ref.controlDependencies?.find(c => c.id === cId && c.when === cWhen)) {
|
|
349
439
|
ref.controlDependencies.push({ ...cd, byIteration: true });
|
|
350
440
|
}
|
|
351
441
|
}
|