@eagleoutice/flowr 2.1.7 → 2.1.9

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 (84) hide show
  1. package/README.md +2 -1
  2. package/abstract-interpretation/normalized-ast-fold.d.ts +124 -0
  3. package/abstract-interpretation/normalized-ast-fold.js +178 -0
  4. package/benchmark/summarizer/first-phase/process.js +6 -5
  5. package/cli/repl/commands/repl-dataflow.js +5 -2
  6. package/cli/repl/commands/repl-normalize.js +5 -2
  7. package/cli/repl/commands/repl-query.js +2 -2
  8. package/cli/repl/server/messages/message-query.js +1 -1
  9. package/cli/slicer-app.js +1 -1
  10. package/core/steps/pipeline/pipeline.d.ts +63 -0
  11. package/dataflow/environments/default-builtin-config.js +45 -6
  12. package/dataflow/environments/environment.d.ts +46 -8
  13. package/dataflow/environments/environment.js +24 -1
  14. package/dataflow/environments/identifier.d.ts +49 -7
  15. package/dataflow/environments/identifier.js +11 -2
  16. package/dataflow/environments/resolve-by-name.d.ts +5 -0
  17. package/dataflow/environments/resolve-by-name.js +14 -0
  18. package/dataflow/extractor.js +5 -4
  19. package/dataflow/graph/dataflowgraph-builder.d.ts +6 -0
  20. package/dataflow/graph/dataflowgraph-builder.js +8 -0
  21. package/dataflow/graph/edge.d.ts +10 -4
  22. package/dataflow/graph/edge.js +12 -5
  23. package/dataflow/graph/graph.d.ts +41 -3
  24. package/dataflow/graph/graph.js +39 -34
  25. package/dataflow/graph/vertex.d.ts +66 -7
  26. package/dataflow/graph/vertex.js +15 -0
  27. package/dataflow/info.d.ts +79 -11
  28. package/dataflow/info.js +20 -0
  29. package/dataflow/internal/linker.d.ts +4 -2
  30. package/dataflow/internal/linker.js +12 -5
  31. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +2 -0
  32. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +5 -3
  33. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +1 -1
  34. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.d.ts +16 -0
  35. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +83 -6
  36. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +17 -7
  37. package/dataflow/internal/process/functions/call/common.js +1 -1
  38. package/documentation/doc-util/doc-dfg.d.ts +2 -2
  39. package/documentation/doc-util/doc-dfg.js +11 -16
  40. package/documentation/doc-util/doc-normalized-ast.js +1 -1
  41. package/documentation/doc-util/doc-types.d.ts +1 -1
  42. package/documentation/doc-util/doc-types.js +21 -0
  43. package/documentation/print-capabilities-markdown.js +1 -1
  44. package/documentation/print-dataflow-graph-wiki.js +44 -7
  45. package/documentation/print-linting-and-testing-wiki.js +60 -26
  46. package/documentation/print-normalized-ast-wiki.js +107 -5
  47. package/documentation/print-query-wiki.js +8 -1
  48. package/package.json +17 -3
  49. package/queries/catalog/call-context-query/call-context-query-executor.js +23 -2
  50. package/queries/catalog/call-context-query/call-context-query-format.d.ts +29 -2
  51. package/queries/catalog/call-context-query/call-context-query-format.js +7 -1
  52. package/queries/catalog/call-context-query/cascade-action.d.ts +8 -0
  53. package/queries/catalog/call-context-query/cascade-action.js +13 -0
  54. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.d.ts +11 -1
  55. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +41 -4
  56. package/queries/catalog/dependencies-query/dependencies-query-format.js +4 -0
  57. package/queries/query.d.ts +4 -4
  58. package/queries/query.js +17 -5
  59. package/r-bridge/lang-4.x/ast/model/model.d.ts +3 -0
  60. package/r-bridge/lang-4.x/ast/model/nodes/r-number.d.ts +5 -1
  61. package/r-bridge/lang-4.x/ast/model/processing/node-id.d.ts +6 -1
  62. package/r-bridge/lang-4.x/ast/model/processing/node-id.js +6 -1
  63. package/r-bridge/lang-4.x/ast/model/processing/visitor.d.ts +1 -1
  64. package/r-bridge/lang-4.x/ast/model/processing/visitor.js +1 -1
  65. package/r-bridge/lang-4.x/ast/parser/json/format.js +2 -2
  66. package/reconstruct/reconstruct.js +1 -1
  67. package/slicing/static/slice-call.d.ts +7 -2
  68. package/slicing/static/slice-call.js +33 -44
  69. package/slicing/static/static-slicer.d.ts +5 -1
  70. package/slicing/static/static-slicer.js +22 -8
  71. package/slicing/static/visiting-queue.d.ts +4 -4
  72. package/slicing/static/visiting-queue.js +5 -3
  73. package/statistics/output/print-stats.js +2 -1
  74. package/statistics/summarizer/post-process/histogram.js +2 -1
  75. package/statistics/summarizer/post-process/post-process-output.js +2 -1
  76. package/statistics/summarizer/second-phase/process.js +3 -3
  77. package/util/arrays.d.ts +1 -1
  78. package/util/arrays.js +3 -3
  79. package/util/assert.d.ts +1 -1
  80. package/util/assert.js +3 -2
  81. package/util/cfg/cfg.js +4 -2
  82. package/util/mermaid/cfg.js +1 -1
  83. package/util/summarizer.js +2 -2
  84. package/util/version.js +1 -1
@@ -5,7 +5,6 @@ exports.isPositionalArgument = isPositionalArgument;
5
5
  exports.isNamedArgument = isNamedArgument;
6
6
  exports.getReferenceOfArgument = getReferenceOfArgument;
7
7
  const assert_1 = require("../../util/assert");
8
- const edge_1 = require("./edge");
9
8
  const diff_1 = require("./diff");
10
9
  const vertex_1 = require("./vertex");
11
10
  const arrays_1 = require("../../util/arrays");
@@ -16,23 +15,27 @@ const clone_1 = require("../environments/clone");
16
15
  const json_1 = require("../../util/json");
17
16
  const built_in_1 = require("../environments/built-in");
18
17
  const logger_1 = require("../logger");
18
+ /**
19
+ * Check if the given argument is a {@link PositionalFunctionArgument}.
20
+ */
19
21
  function isPositionalArgument(arg) {
20
22
  return arg !== r_function_call_1.EmptyArgument && arg.name === undefined;
21
23
  }
24
+ /**
25
+ * Check if the given argument is a {@link NamedFunctionArgument}.
26
+ */
22
27
  function isNamedArgument(arg) {
23
28
  return arg !== r_function_call_1.EmptyArgument && arg.name !== undefined;
24
29
  }
30
+ /**
31
+ * Returns the reference of a non-empty argument.
32
+ */
25
33
  function getReferenceOfArgument(arg) {
26
34
  if (arg !== r_function_call_1.EmptyArgument) {
27
- return arg.nodeId;
35
+ return arg?.nodeId;
28
36
  }
29
37
  return undefined;
30
38
  }
31
- function extractEdgeIds(from, to) {
32
- const fromId = typeof from === 'object' ? from.nodeId : from;
33
- const toId = typeof to === 'object' ? to.nodeId : to;
34
- return { fromId, toId };
35
- }
36
39
  /**
37
40
  * The dataflow graph holds the dataflow information found within the given AST.
38
41
  * We differentiate the directed edges in {@link EdgeType} and the vertices indicated by {@link DataflowGraphVertexArgument}
@@ -43,6 +46,11 @@ function extractEdgeIds(from, to) {
43
46
  * However, this does not have to hold during the construction as edges may point from or to vertices which are yet to be constructed.
44
47
  *
45
48
  * All methods return the modified graph to allow for chaining.
49
+ *
50
+ * @see {@link DataflowGraph#addEdge|`addEdge`} - to add an edge to the graph
51
+ * @see {@link DataflowGraph#addVertex|`addVertex`} - to add a vertex to the graph
52
+ * @see {@link DataflowGraph#fromJson|`fromJson`} - to construct a dataflow graph object from a deserialized JSON object.
53
+ * @see {@link emptyGraph} - to create an empty graph (useful in tests)
46
54
  */
47
55
  class DataflowGraph {
48
56
  static DEFAULT_ENVIRONMENT = undefined;
@@ -185,13 +193,10 @@ class DataflowGraph {
185
193
  return this;
186
194
  }
187
195
  /**
188
- * Will insert a new edge into the graph,
189
- * if the direction of the edge is of no importance (`same-read-read` or `same-def-def`), source
190
- * and target will be sorted so that `from` has the lower, and `to` the higher id (default ordering).
191
196
  * Please note that this will never make edges to {@link BuiltIn} as they are not part of the graph.
192
197
  */
193
198
  addEdge(from, to, type) {
194
- const { fromId, toId } = extractEdgeIds(from, to);
199
+ const [fromId, toId] = extractEdgeIds(from, to);
195
200
  if (fromId === toId || toId === built_in_1.BuiltIn) {
196
201
  return this;
197
202
  }
@@ -206,7 +211,6 @@ class DataflowGraph {
206
211
  else {
207
212
  existingFrom.set(toId, edge);
208
213
  }
209
- this.installEdge(type, toId, fromId, edge);
210
214
  }
211
215
  else {
212
216
  // adding the type
@@ -214,21 +218,6 @@ class DataflowGraph {
214
218
  }
215
219
  return this;
216
220
  }
217
- installEdge(type, toId, fromId, edge) {
218
- if (type === edge_1.EdgeType.DefinesOnCall) {
219
- const otherEdge = {
220
- ...edge,
221
- types: edge_1.EdgeType.DefinedByOnCall
222
- };
223
- const existingTo = this.edgeInformation.get(toId);
224
- if (existingTo === undefined) {
225
- this.edgeInformation.set(toId, new Map([[fromId, otherEdge]]));
226
- }
227
- else {
228
- existingTo.set(fromId, otherEdge);
229
- }
230
- }
231
- }
232
221
  /**
233
222
  * Merges the other graph into *this* one (in-place). The return value is only for convenience.
234
223
  *
@@ -269,7 +258,7 @@ class DataflowGraph {
269
258
  existing.set(target, edge);
270
259
  }
271
260
  else {
272
- get.types = get.types | edge.types;
261
+ get.types |= edge.types;
273
262
  }
274
263
  }
275
264
  }
@@ -304,8 +293,17 @@ class DataflowGraph {
304
293
  const vertex = this.getVertex(from, true);
305
294
  (0, assert_1.guard)(vertex !== undefined, () => `node must be defined for ${from} to add control dependency`);
306
295
  vertex.controlDependencies ??= [];
307
- if (to && vertex.controlDependencies.every(({ id, when: cond }) => id !== to && when !== cond)) {
308
- vertex.controlDependencies.push({ id: to, when });
296
+ if (to) {
297
+ let hasControlDependency = false;
298
+ for (const { id, when: cond } of vertex.controlDependencies) {
299
+ if (id === to && when !== cond) {
300
+ hasControlDependency = true;
301
+ break;
302
+ }
303
+ }
304
+ if (!hasControlDependency) {
305
+ vertex.controlDependencies.push({ id: to, when });
306
+ }
309
307
  }
310
308
  return this;
311
309
  }
@@ -334,8 +332,8 @@ class DataflowGraph {
334
332
  exports.DataflowGraph = DataflowGraph;
335
333
  function mergeNodeInfos(current, next) {
336
334
  if (current.tag !== next.tag) {
337
- logger_1.dataflowLogger.warn(`nodes to be joined for the same id should have the same tag, but ${JSON.stringify(current, json_1.jsonReplacer)} vs. ${JSON.stringify(next, json_1.jsonReplacer)} -- we are currently not handling cases in which vertices may be either! Keeping current.`);
338
- return { ...current };
335
+ logger_1.dataflowLogger.warn(() => `nodes to be joined for the same id should have the same tag, but ${JSON.stringify(current, json_1.jsonReplacer)} vs. ${JSON.stringify(next, json_1.jsonReplacer)} -- we are currently not handling cases in which vertices may be either! Keeping current.`);
336
+ return current;
339
337
  }
340
338
  if (current.tag === vertex_1.VertexType.VariableDefinition) {
341
339
  (0, assert_1.guard)(current.scope === next.scope, 'nodes to be joined for the same id must have the same scope');
@@ -347,7 +345,14 @@ function mergeNodeInfos(current, next) {
347
345
  (0, assert_1.guard)(current.scope === next.scope, 'nodes to be joined for the same id must have the same scope');
348
346
  (0, assert_1.guard)((0, arrays_1.arrayEqual)(current.exitPoints, next.exitPoints), 'nodes to be joined must have same exist points');
349
347
  }
350
- // make a copy
351
- return { ...current };
348
+ return current;
349
+ }
350
+ /**
351
+ * Returns the ids of the dataflow vertices referenced by a {@link ReferenceForEdge}.
352
+ */
353
+ function extractEdgeIds(from, to) {
354
+ const fromId = typeof from === 'object' ? from.nodeId : from;
355
+ const toId = typeof to === 'object' ? to.nodeId : to;
356
+ return [fromId, toId];
352
357
  }
353
358
  //# sourceMappingURL=graph.js.map
@@ -3,7 +3,6 @@ import type { DataflowFunctionFlowInformation, FunctionArgument } from './graph'
3
3
  import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
4
4
  import type { REnvironmentInformation } from '../environments/environment';
5
5
  import type { ControlDependency } from '../info';
6
- export type DataflowGraphVertices<Vertex extends DataflowGraphVertexInfo = DataflowGraphVertexInfo> = Map<NodeId, Vertex>;
7
6
  export declare enum VertexType {
8
7
  Value = "value",
9
8
  Use = "use",
@@ -12,7 +11,7 @@ export declare enum VertexType {
12
11
  FunctionDefinition = "function-definition"
13
12
  }
14
13
  /**
15
- * Arguments required to construct a vertex in the dataflow graph.
14
+ * Arguments required to construct a vertex in the {@link DataflowGraph|dataflow graph}.
16
15
  *
17
16
  * @see DataflowGraphVertexUse
18
17
  * @see DataflowGraphVertexVariableDefinition
@@ -24,7 +23,9 @@ interface DataflowGraphVertexBase extends MergeableRecord {
24
23
  */
25
24
  readonly tag: VertexType;
26
25
  /**
27
- * The id of the node (the id assigned by the {@link ParentInformation} decoration)
26
+ * The id of the node (the id assigned by the {@link ParentInformation} decoration).
27
+ * This unanimously identifies the vertex in the {@link DataflowGraph|dataflow graph}
28
+ * as well as the corresponding {@link NormalizedAst|normalized AST}.
28
29
  */
29
30
  id: NodeId;
30
31
  /**
@@ -32,19 +33,40 @@ interface DataflowGraphVertexBase extends MergeableRecord {
32
33
  */
33
34
  environment?: REnvironmentInformation | undefined;
34
35
  /**
35
- * See {@link IdentifierReference}
36
+ * @see {@link ControlDependency} - the collection of control dependencies which have an influence on whether the vertex is executed.
36
37
  */
37
38
  controlDependencies: ControlDependency[] | undefined;
38
39
  }
39
40
  /**
40
41
  * Marker vertex for a value in the dataflow of the program.
42
+ * This does not contain the _value_ of the referenced constant
43
+ * as this is available with the {@link DataflowGraphVertexBase#id|id} in the {@link NormalizedAst|normalized AST}
44
+ * (or more specifically the {@link AstIdMap}).
45
+ *
46
+ * If you have a {@link DataflowGraph|dataflow graph} named `graph`
47
+ * with an {@link AstIdMap} and a value vertex object with name `value` the following Code should work:
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const node = graph.idMap.get(value.id)
52
+ * ```
53
+ *
54
+ * This then returns the corresponding node in the {@link NormalizedAst|normalized AST}, for example,
55
+ * an {@link RNumber} or {@link RString}.
56
+ *
57
+ * This works similarly for {@link IdentifierReference|identifier references}
58
+ * for which you can use the {@link IdentifierReference#nodeId|`nodeId`}.
59
+ *
60
+ * @see {@link isValueVertex} - to check if a vertex is a value vertex
41
61
  */
42
62
  export interface DataflowGraphVertexValue extends DataflowGraphVertexBase {
43
63
  readonly tag: VertexType.Value;
44
64
  readonly environment?: undefined;
45
65
  }
46
66
  /**
47
- * Arguments required to construct a vertex which represents the usage of a variable in the dataflow graph.
67
+ * Arguments required to construct a vertex which represents the usage of a variable in the {@link DataflowGraph|dataflow graph}.
68
+ *
69
+ * @see {@link isUseVertex} - to check if a vertex is a use vertex
48
70
  */
49
71
  export interface DataflowGraphVertexUse extends DataflowGraphVertexBase {
50
72
  readonly tag: VertexType.Use;
@@ -52,7 +74,9 @@ export interface DataflowGraphVertexUse extends DataflowGraphVertexBase {
52
74
  readonly environment?: undefined;
53
75
  }
54
76
  /**
55
- * Arguments required to construct a vertex which represents the usage of a variable in the dataflow graph.
77
+ * Arguments required to construct a vertex which represents the usage of a variable in the {@link DataflowGraph|dataflow graph}.
78
+ *
79
+ * @see {@link isFunctionCallVertex} - to check if a vertex is a function call vertex
56
80
  */
57
81
  export interface DataflowGraphVertexFunctionCall extends DataflowGraphVertexBase {
58
82
  readonly tag: VertexType.FunctionCall;
@@ -71,13 +95,20 @@ export interface DataflowGraphVertexFunctionCall extends DataflowGraphVertexBase
71
95
  environment: REnvironmentInformation | undefined;
72
96
  }
73
97
  /**
74
- * Arguments required to construct a vertex which represents the definition of a variable in the dataflow graph.
98
+ * Arguments required to construct a vertex which represents the definition of a variable in the {@link DataflowGraph|dataflow graph}.
99
+ *
100
+ * @see {@link isVariableDefinitionVertex} - to check if a vertex is a variable definition vertex
75
101
  */
76
102
  export interface DataflowGraphVertexVariableDefinition extends DataflowGraphVertexBase {
77
103
  readonly tag: VertexType.VariableDefinition;
78
104
  /** Does not require an environment, those are attached to the call */
79
105
  readonly environment?: undefined;
80
106
  }
107
+ /**
108
+ * Arguments required to construct a vertex which represents the definition of a function in the {@link DataflowGraph|dataflow graph}.
109
+ *
110
+ * @see {@link isFunctionDefinitionVertex} - to check if a vertex is a function definition vertex
111
+ */
81
112
  export interface DataflowGraphVertexFunctionDefinition extends DataflowGraphVertexBase {
82
113
  readonly tag: VertexType.FunctionDefinition;
83
114
  /**
@@ -93,11 +124,39 @@ export interface DataflowGraphVertexFunctionDefinition extends DataflowGraphVert
93
124
  exitPoints: readonly NodeId[];
94
125
  environment?: REnvironmentInformation;
95
126
  }
127
+ /**
128
+ * What is to be passed to construct a vertex in the {@link DataflowGraph|dataflow graph}
129
+ */
96
130
  export type DataflowGraphVertexArgument = DataflowGraphVertexUse | DataflowGraphVertexVariableDefinition | DataflowGraphVertexFunctionDefinition | DataflowGraphVertexFunctionCall | DataflowGraphVertexValue;
131
+ /**
132
+ * This is the union type of all possible vertices that appear within a {@link DataflowGraph|dataflow graph},
133
+ * they can be constructed passing a {@link DataflowGraphVertexArgument} to the graph.
134
+ *
135
+ * See {@link DataflowGraphVertices} for an id-based mapping.
136
+ */
97
137
  export type DataflowGraphVertexInfo = Required<DataflowGraphVertexArgument>;
138
+ /**
139
+ * A mapping of {@link NodeId}s to {@link DataflowGraphVertexInfo|vertices}.
140
+ */
141
+ export type DataflowGraphVertices<Vertex extends DataflowGraphVertexInfo = DataflowGraphVertexInfo> = Map<NodeId, Vertex>;
142
+ /**
143
+ * Check if the given vertex is a {@link DataflowGraphVertexValue|value vertex}.
144
+ */
98
145
  export declare function isValueVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexValue;
146
+ /**
147
+ * Check if the given vertex is a {@link DataflowGraphVertexUse|use vertex}.
148
+ */
99
149
  export declare function isUseVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexUse;
150
+ /**
151
+ * Check if the given vertex is a {@link DataflowGraphVertexFunctionCall|function call vertex}.
152
+ */
100
153
  export declare function isFunctionCallVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexFunctionCall;
154
+ /**
155
+ * Check if the given vertex is a {@link DataflowGraphVertexVariableDefinition|variable definition vertex}.
156
+ */
101
157
  export declare function isVariableDefinitionVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexVariableDefinition;
158
+ /**
159
+ * Check if the given vertex is a {@link DataflowGraphVertexFunctionDefinition|function definition vertex}.
160
+ */
102
161
  export declare function isFunctionDefinitionVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexFunctionDefinition;
103
162
  export {};
@@ -14,18 +14,33 @@ var VertexType;
14
14
  VertexType["VariableDefinition"] = "variable-definition";
15
15
  VertexType["FunctionDefinition"] = "function-definition";
16
16
  })(VertexType || (exports.VertexType = VertexType = {}));
17
+ /**
18
+ * Check if the given vertex is a {@link DataflowGraphVertexValue|value vertex}.
19
+ */
17
20
  function isValueVertex(vertex) {
18
21
  return vertex.tag === VertexType.Value;
19
22
  }
23
+ /**
24
+ * Check if the given vertex is a {@link DataflowGraphVertexUse|use vertex}.
25
+ */
20
26
  function isUseVertex(vertex) {
21
27
  return vertex.tag === VertexType.Use;
22
28
  }
29
+ /**
30
+ * Check if the given vertex is a {@link DataflowGraphVertexFunctionCall|function call vertex}.
31
+ */
23
32
  function isFunctionCallVertex(vertex) {
24
33
  return vertex.tag === VertexType.FunctionCall;
25
34
  }
35
+ /**
36
+ * Check if the given vertex is a {@link DataflowGraphVertexVariableDefinition|variable definition vertex}.
37
+ */
26
38
  function isVariableDefinitionVertex(vertex) {
27
39
  return vertex.tag === VertexType.VariableDefinition;
28
40
  }
41
+ /**
42
+ * Check if the given vertex is a {@link DataflowGraphVertexFunctionDefinition|function definition vertex}.
43
+ */
29
44
  function isFunctionDefinitionVertex(vertex) {
30
45
  return vertex.tag === VertexType.FunctionDefinition;
31
46
  }
@@ -4,6 +4,24 @@ 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
+ /**
8
+ * A control dependency links a vertex to the control flow element which
9
+ * may have an influence on its execution.
10
+ * 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
+ */
14
+ export interface ControlDependency {
15
+ /** The id of the node that causes the control dependency to be active (e.g., the condition of an if) */
16
+ readonly id: NodeId;
17
+ /** when does this control dependency trigger (if the condition is true or false)? */
18
+ readonly when?: boolean;
19
+ }
20
+ /**
21
+ * Classifies the type of exit point encountered.
22
+ *
23
+ * @see {@link ExitPoint}
24
+ */
7
25
  export declare const enum ExitPointType {
8
26
  /** The exit point is the implicit (last executed expression of a function/block) */
9
27
  Default = 0,
@@ -14,20 +32,31 @@ export declare const enum ExitPointType {
14
32
  /** The exit point is an explicit `next` call (or an alias of it) */
15
33
  Next = 3
16
34
  }
17
- export interface ControlDependency {
18
- /** The id of the node that causes the control dependency to be active (e.g., the condition of an if) */
19
- readonly id: NodeId;
20
- /** when does this control dependency trigger (if the condition is true or false)? */
21
- readonly when?: boolean;
22
- }
35
+ /**
36
+ * An exit point describes the position which ends the current control flow structure.
37
+ * This may be as innocent as the last expression or explicit with a `return`/`break`/`next`.
38
+ *
39
+ * @see {@link ExitPointType} - for the different types of exit points
40
+ * @see {@link addNonDefaultExitPoints} - to easily modify lists of exit points
41
+ * @see {@link alwaysExits} - to check whether a list of control dependencies always triggers an exit
42
+ * @see {@link filterOutLoopExitPoints} - to remove loop exit points from a list
43
+ */
23
44
  export interface ExitPoint {
24
45
  /** What kind of exit point is this one? May be used to filter for exit points of specific causes. */
25
46
  readonly type: ExitPointType;
26
47
  /** The id of the node which causes the exit point! */
27
48
  readonly nodeId: NodeId;
28
- /** Control dependencies which influence if the exit point triggers (e.g., if the `return` is contained within an `if` statement) */
49
+ /**
50
+ * Control dependencies which influence if the exit point triggers
51
+ * (e.g., if the `return` is contained within an `if` statement).
52
+ *
53
+ * @see {@link happensInEveryBranch} - to check whether control dependencies are exhaustive
54
+ */
29
55
  readonly controlDependencies: ControlDependency[] | undefined;
30
56
  }
57
+ /**
58
+ * Adds all non-default exit points to the existing list.
59
+ */
31
60
  export declare function addNonDefaultExitPoints(existing: ExitPoint[], add: readonly ExitPoint[]): void;
32
61
  /** The control flow information for the current DataflowInformation. */
33
62
  export interface DataflowCfgInformation {
@@ -37,24 +66,63 @@ export interface DataflowCfgInformation {
37
66
  exitPoints: readonly ExitPoint[];
38
67
  }
39
68
  /**
40
- * The dataflow information is continuously updated during the dataflow analysis
69
+ * The dataflow information is one of the fundamental structures we have in the dataflow analysis.
70
+ * It is continuously updated during the dataflow analysis
41
71
  * and holds its current state for the respective subtree processed.
72
+ * Each processor during the dataflow analysis may use the information from its children
73
+ * to produce a new state of the dataflow information.
74
+ *
75
+ * You may initialize a new dataflow information with {@link initializeCleanDataflowInformation}.
76
+ *
77
+ * @see {@link DataflowCfgInformation} - the control flow aspects
42
78
  */
43
79
  export interface DataflowInformation extends DataflowCfgInformation {
44
- /** References that have not been identified as read or write and will be so on higher */
80
+ /**
81
+ * References that have not been identified as read or write and will be so on higher processors.
82
+ *
83
+ * For example, when we analyze the `x` vertex in `x <- 3`, we will first create an unknown reference for `x`
84
+ * as we have not yet seen the assignment!
85
+ *
86
+ * @see {@link IdentifierReference} - a reference on a variable, parameter, function call, ...
87
+ */
45
88
  unknownReferences: readonly IdentifierReference[];
46
- /** References which are read */
89
+ /**
90
+ * References which are read within the current subtree.
91
+ *
92
+ * @see {@link IdentifierReference} - a reference on a variable, parameter, function call, ...
93
+ * */
47
94
  in: readonly IdentifierReference[];
48
- /** References which are written to */
95
+ /**
96
+ * References which are written to within the current subtree
97
+ *
98
+ * @see {@link IdentifierReference} - a reference on a variable, parameter, function call, ...
99
+ */
49
100
  out: readonly IdentifierReference[];
50
101
  /** Current environments used for name resolution, probably updated on the next expression-list processing */
51
102
  environment: REnvironmentInformation;
52
103
  /** The current constructed dataflow graph */
53
104
  graph: DataflowGraph;
54
105
  }
106
+ /**
107
+ * Initializes an empty {@link DataflowInformation} object with the given entry point and data.
108
+ * This is to be used as a "starting point" when processing leaf nodes during the dataflow extraction.
109
+ *
110
+ * @see {@link DataflowInformation}
111
+ */
55
112
  export declare function initializeCleanDataflowInformation<T>(entryPoint: NodeId, data: Pick<DataflowProcessorInformation<T>, 'environment' | 'completeAst'>): DataflowInformation;
113
+ /**
114
+ * Checks whether the given control dependencies are exhaustive (i.e. if for every control dependency on a boolean,
115
+ * the list contains a dependency on the `true` and on the `false` case).
116
+ */
56
117
  export declare function happensInEveryBranch(controlDependencies: readonly ControlDependency[] | undefined): boolean;
118
+ /**
119
+ * Checks whether the given dataflow information always exits (i.e., if there is a non-default exit point in every branch).
120
+ * @see {@link ExitPoint} - for the different types of exit points
121
+ */
57
122
  export declare function alwaysExits(data: DataflowInformation): boolean;
123
+ /**
124
+ * Filters out exit points which end their cascade within a loop.
125
+ */
58
126
  export declare function filterOutLoopExitPoints(exitPoints: readonly ExitPoint[]): readonly ExitPoint[];
59
127
  export declare function diffControlDependency<Report extends WriteableDifferenceReport>(a: ControlDependency | undefined, b: ControlDependency | undefined, info: GenericDifferenceInformation<Report>): void;
60
128
  export declare function diffControlDependencies<Report extends WriteableDifferenceReport>(a: ControlDependency[] | undefined, b: ControlDependency[] | undefined, info: GenericDifferenceInformation<Report>): void;
package/dataflow/info.js CHANGED
@@ -8,9 +8,18 @@ exports.filterOutLoopExitPoints = filterOutLoopExitPoints;
8
8
  exports.diffControlDependency = diffControlDependency;
9
9
  exports.diffControlDependencies = diffControlDependencies;
10
10
  const graph_1 = require("./graph/graph");
11
+ /**
12
+ * Adds all non-default exit points to the existing list.
13
+ */
11
14
  function addNonDefaultExitPoints(existing, add) {
12
15
  existing.push(...add.filter(({ type }) => type !== 0 /* ExitPointType.Default */));
13
16
  }
17
+ /**
18
+ * Initializes an empty {@link DataflowInformation} object with the given entry point and data.
19
+ * This is to be used as a "starting point" when processing leaf nodes during the dataflow extraction.
20
+ *
21
+ * @see {@link DataflowInformation}
22
+ */
14
23
  function initializeCleanDataflowInformation(entryPoint, data) {
15
24
  return {
16
25
  unknownReferences: [],
@@ -22,6 +31,10 @@ function initializeCleanDataflowInformation(entryPoint, data) {
22
31
  exitPoints: [{ nodeId: entryPoint, type: 0 /* ExitPointType.Default */, controlDependencies: undefined }]
23
32
  };
24
33
  }
34
+ /**
35
+ * Checks whether the given control dependencies are exhaustive (i.e. if for every control dependency on a boolean,
36
+ * the list contains a dependency on the `true` and on the `false` case).
37
+ */
25
38
  function happensInEveryBranch(controlDependencies) {
26
39
  if (controlDependencies === undefined) {
27
40
  /* the cds are unconstrained */
@@ -43,9 +56,16 @@ function happensInEveryBranch(controlDependencies) {
43
56
  }
44
57
  return trues.every(id => falseSet.has(id));
45
58
  }
59
+ /**
60
+ * Checks whether the given dataflow information always exits (i.e., if there is a non-default exit point in every branch).
61
+ * @see {@link ExitPoint} - for the different types of exit points
62
+ */
46
63
  function alwaysExits(data) {
47
64
  return data.exitPoints?.some(e => e.type !== 0 /* ExitPointType.Default */ && happensInEveryBranch(e.controlDependencies)) ?? false;
48
65
  }
66
+ /**
67
+ * Filters out exit points which end their cascade within a loop.
68
+ */
49
69
  function filterOutLoopExitPoints(exitPoints) {
50
70
  return exitPoints.filter(({ type }) => type === 1 /* ExitPointType.Return */ || type === 0 /* ExitPointType.Default */);
51
71
  }
@@ -23,8 +23,10 @@ export declare function linkFunctionCalls(graph: DataflowGraph, idMap: AstIdMap,
23
23
  functionCall: NodeId;
24
24
  called: readonly DataflowGraphVertexInfo[];
25
25
  }[];
26
- /** convenience function returning all known call targets */
27
- export declare function getAllFunctionCallTargets(call: NodeId, graph: DataflowGraph): NodeId[];
26
+ /**
27
+ * convenience function returning all known call targets, as well as the name source which defines them
28
+ */
29
+ export declare function getAllFunctionCallTargets(call: NodeId, graph: DataflowGraph, environment?: REnvironmentInformation): NodeId[];
28
30
  export declare function getAllLinkedFunctionDefinitions(functionDefinitionReadIds: ReadonlySet<NodeId>, dataflowGraph: DataflowGraph): Set<DataflowGraphVertexInfo>;
29
31
  /**
30
32
  * This method links a set of read variables to definitions in an environment.
@@ -84,11 +84,13 @@ function linkArgumentsOnCall(args, params, graph) {
84
84
  if (param !== undefined) {
85
85
  logger_1.dataflowLogger.trace(`mapping named argument "${name}" to parameter "${param.name.content}"`);
86
86
  graph.addEdge(arg.nodeId, param.name.info.id, edge_1.EdgeType.DefinesOnCall);
87
+ graph.addEdge(param.name.info.id, arg.nodeId, edge_1.EdgeType.DefinedByOnCall);
87
88
  matchedParameters.add(name);
88
89
  }
89
90
  else if (specialDotParameter !== undefined) {
90
91
  logger_1.dataflowLogger.trace(`mapping named argument "${name}" to dot-dot-dot parameter`);
91
92
  graph.addEdge(arg.nodeId, specialDotParameter.name.info.id, edge_1.EdgeType.DefinesOnCall);
93
+ graph.addEdge(specialDotParameter.name.info.id, arg.nodeId, edge_1.EdgeType.DefinedByOnCall);
92
94
  }
93
95
  }
94
96
  const remainingParameter = params.filter(p => !matchedParameters.has(p.name.content));
@@ -103,6 +105,7 @@ function linkArgumentsOnCall(args, params, graph) {
103
105
  if (specialDotParameter !== undefined) {
104
106
  logger_1.dataflowLogger.trace(`mapping unnamed argument ${i} (id: ${arg.nodeId}) to dot-dot-dot parameter`);
105
107
  graph.addEdge(arg.nodeId, specialDotParameter.name.info.id, edge_1.EdgeType.DefinesOnCall);
108
+ graph.addEdge(specialDotParameter.name.info.id, arg.nodeId, edge_1.EdgeType.DefinedByOnCall);
106
109
  }
107
110
  else {
108
111
  logger_1.dataflowLogger.warn(`skipping argument ${i} as there is no corresponding parameter - R should block that`);
@@ -112,6 +115,7 @@ function linkArgumentsOnCall(args, params, graph) {
112
115
  const param = remainingParameter[i];
113
116
  logger_1.dataflowLogger.trace(`mapping unnamed argument ${i} (id: ${arg.nodeId}) to parameter "${param.name.content}"`);
114
117
  graph.addEdge(arg.nodeId, param.name.info.id, edge_1.EdgeType.DefinesOnCall);
118
+ graph.addEdge(param.name.info.id, arg.nodeId, edge_1.EdgeType.DefinedByOnCall);
115
119
  }
116
120
  }
117
121
  function linkFunctionCallArguments(targetId, idMap, functionCallName, functionRootId, callArgs, finalGraph) {
@@ -138,7 +142,8 @@ function linkFunctionCallWithSingleTarget(graph, def, info, idMap) {
138
142
  continue;
139
143
  }
140
144
  for (const def of defs) {
141
- graph.addEdge(id, def, edge_1.EdgeType.Reads);
145
+ graph.addEdge(ingoing, def, edge_1.EdgeType.DefinedByOnCall);
146
+ graph.addEdge(id, def, edge_1.EdgeType.DefinesOnCall);
142
147
  }
143
148
  }
144
149
  }
@@ -187,8 +192,10 @@ function linkFunctionCalls(graph, idMap, thisGraph) {
187
192
  }
188
193
  return calledFunctionDefinitions;
189
194
  }
190
- /** convenience function returning all known call targets */
191
- function getAllFunctionCallTargets(call, graph) {
195
+ /**
196
+ * convenience function returning all known call targets, as well as the name source which defines them
197
+ */
198
+ function getAllFunctionCallTargets(call, graph, environment) {
192
199
  const found = [];
193
200
  const callVertex = graph.get(call, true);
194
201
  if (callVertex === undefined) {
@@ -198,8 +205,8 @@ function getAllFunctionCallTargets(call, graph) {
198
205
  if (info.tag !== vertex_1.VertexType.FunctionCall) {
199
206
  return [];
200
207
  }
201
- if (info.name !== undefined && info.environment !== undefined) {
202
- const functionCallDefs = (0, resolve_by_name_1.resolveByName)(info.name, info.environment, identifier_1.ReferenceType.Function)?.map(d => d.nodeId) ?? [];
208
+ if (info.name !== undefined && (environment !== undefined || info.environment !== undefined)) {
209
+ const functionCallDefs = (0, resolve_by_name_1.resolveByName)(info.name, environment ?? info.environment, identifier_1.ReferenceType.Function)?.map(d => d.nodeId) ?? [];
203
210
  for (const [target, outgoingEdge] of outgoingEdges.entries()) {
204
211
  if ((0, edge_1.edgeIncludesType)(outgoingEdge.types, edge_1.EdgeType.Calls)) {
205
212
  functionCallDefs.push(target);
@@ -15,6 +15,8 @@ export interface AssignmentConfiguration extends ForceArguments {
15
15
  readonly makeMaybe?: boolean;
16
16
  readonly quoteSource?: boolean;
17
17
  readonly canBeReplacement?: boolean;
18
+ /** is the target a variable pointing at the actual name? */
19
+ readonly targetVariable?: boolean;
18
20
  }
19
21
  /**
20
22
  * Processes an assignment, i.e., `<target> <- <source>`.
@@ -57,7 +57,7 @@ args, rootId, data, config) {
57
57
  const effectiveArgs = getEffectiveOrder(config, args);
58
58
  const { target, source } = extractSourceAndTarget(effectiveArgs, name);
59
59
  const { type, named } = target;
60
- if (type === type_1.RType.Symbol) {
60
+ if (!config.targetVariable && type === type_1.RType.Symbol) {
61
61
  const res = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, reverseOrder: !config.swapSourceAndTarget, forceArgs: config.forceArgs });
62
62
  return processAssignmentToSymbol({
63
63
  ...config,
@@ -107,8 +107,10 @@ args, rootId, data, config) {
107
107
  else if (type === type_1.RType.String) {
108
108
  return processAssignmentToString(target, args, name, rootId, data, config, source);
109
109
  }
110
- logger_1.dataflowLogger.warn(`Assignment ${name.content} has an unknown target type ${target.type}, skipping`);
111
- return (0, known_call_handling_1.processKnownFunctionCall)({ name, args: effectiveArgs, rootId, data, forceArgs: config.forceArgs }).information;
110
+ logger_1.dataflowLogger.warn(`Assignment ${name.content} has an unknown target type ${target.type} => unknown impact`);
111
+ const info = (0, known_call_handling_1.processKnownFunctionCall)({ name, args: effectiveArgs, rootId, data, forceArgs: config.forceArgs }).information;
112
+ info.graph.markIdForUnknownSideEffects(rootId);
113
+ return info;
112
114
  }
113
115
  function extractSourceAndTarget(args, name) {
114
116
  const source = (0, unpack_argument_1.unpackArgument)(args[1], false);
@@ -53,7 +53,7 @@ function updateSideEffectsForCalledFunctions(calledEnvs, inputEnvironment, nextG
53
53
  for (const { functionCall, called } of calledEnvs) {
54
54
  const callDependencies = nextGraph.getVertex(functionCall, true)?.controlDependencies;
55
55
  for (const calledFn of called) {
56
- (0, assert_1.guard)(calledFn.tag === vertex_1.VertexType.FunctionDefinition, 'called function must call a function definition');
56
+ (0, assert_1.guard)(calledFn.tag === vertex_1.VertexType.FunctionDefinition, 'called function must be a function definition');
57
57
  // only merge the environments they have in common
58
58
  let environment = calledFn.environment;
59
59
  while (environment.level > inputEnvironment.level) {
@@ -4,4 +4,20 @@ import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/
4
4
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
5
5
  import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
6
6
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
+ import { DataflowGraph } from '../../../../../graph/graph';
8
+ import type { REnvironmentInformation } from '../../../../../environments/environment';
7
9
  export declare function processFunctionDefinition<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
10
+ export declare function retrieveActiveEnvironment(callerEnvironment: REnvironmentInformation | undefined, baseEnvironment: REnvironmentInformation): REnvironmentInformation;
11
+ /**
12
+ * Update the closure links of all nested function definitions
13
+ * @param graph - dataflow graph to collect the function definitions from and to update the closure links for
14
+ * @param outEnvironment - active environment on resolving closures (i.e., exit of the function definition)
15
+ * @param fnId - id of the function definition to update the closure links for
16
+ */
17
+ export declare function updateNestedFunctionClosures(graph: DataflowGraph, outEnvironment: REnvironmentInformation, fnId: NodeId): void;
18
+ /**
19
+ * Update the closure links of all nested function calls, this is probably to be done once at the end of the script
20
+ * @param graph - dataflow graph to collect the function calls from and to update the closure links for
21
+ * @param outEnvironment - active environment on resolving closures (i.e., exit of the function definition)
22
+ */
23
+ export declare function updateNestedFunctionCalls(graph: DataflowGraph, outEnvironment: REnvironmentInformation): void;