@eagleoutice/flowr 2.0.16 → 2.0.18

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 (71) hide show
  1. package/README.md +1 -1
  2. package/cli/repl/commands/commands.js +3 -1
  3. package/cli/repl/commands/lineage.d.ts +15 -0
  4. package/cli/repl/commands/lineage.js +66 -0
  5. package/cli/repl/server/connection.d.ts +1 -0
  6. package/cli/repl/server/connection.js +36 -2
  7. package/cli/repl/server/messages/lineage.d.ts +16 -0
  8. package/cli/repl/server/messages/lineage.js +17 -0
  9. package/cli/repl/server/messages/messages.d.ts +2 -1
  10. package/cli/slicer-app.js +2 -2
  11. package/dataflow/environments/built-in.js +29 -9
  12. package/dataflow/environments/environment.js +4 -3
  13. package/dataflow/environments/resolve-by-name.d.ts +1 -1
  14. package/dataflow/environments/resolve-by-name.js +4 -4
  15. package/dataflow/graph/diff.js +1 -0
  16. package/dataflow/graph/graph.d.ts +24 -20
  17. package/dataflow/graph/graph.js +51 -24
  18. package/dataflow/graph/vertex.d.ts +6 -1
  19. package/dataflow/graph/vertex.js +21 -0
  20. package/dataflow/info.d.ts +1 -1
  21. package/dataflow/internal/process/functions/call/built-in/built-in-access.d.ts +2 -1
  22. package/dataflow/internal/process/functions/call/built-in/built-in-access.js +29 -7
  23. package/dataflow/internal/process/functions/call/built-in/built-in-apply.d.ts +14 -0
  24. package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +65 -0
  25. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +19 -2
  26. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +40 -24
  27. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +1 -1
  28. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +10 -1
  29. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +4 -4
  30. package/dataflow/internal/process/functions/call/built-in/built-in-library.js +7 -3
  31. package/dataflow/internal/process/functions/call/built-in/built-in-quote.d.ts +3 -2
  32. package/dataflow/internal/process/functions/call/built-in/built-in-quote.js +1 -1
  33. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.d.ts +3 -2
  34. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +2 -1
  35. package/dataflow/internal/process/functions/call/built-in/built-in-source.d.ts +2 -2
  36. package/dataflow/internal/process/functions/call/built-in/built-in-source.js +18 -7
  37. package/dataflow/internal/process/functions/call/built-in/{built-in-logical-bin-op.d.ts → built-in-special-bin-op.d.ts} +2 -1
  38. package/dataflow/internal/process/functions/call/built-in/{built-in-logical-bin-op.js → built-in-special-bin-op.js} +3 -3
  39. package/dataflow/internal/process/functions/call/common.d.ts +6 -2
  40. package/dataflow/internal/process/functions/call/common.js +36 -1
  41. package/dataflow/internal/process/functions/call/known-call-handling.d.ts +8 -3
  42. package/dataflow/internal/process/functions/call/known-call-handling.js +10 -7
  43. package/dataflow/internal/process/functions/call/named-call-handling.js +3 -1
  44. package/dataflow/internal/process/functions/call/unnamed-call-handling.js +1 -0
  45. package/dataflow/internal/process/functions/process-argument.js +0 -28
  46. package/package.json +3 -2
  47. package/r-bridge/data/data.d.ts +10 -0
  48. package/r-bridge/data/data.js +12 -0
  49. package/r-bridge/lang-4.x/ast/model/operators.js +1 -1
  50. package/reconstruct/auto-select/auto-select-defaults.d.ts +1 -6
  51. package/reconstruct/auto-select/auto-select-defaults.js +1 -13
  52. package/reconstruct/reconstruct.js +1 -1
  53. package/slicing/criterion/parse.d.ts +3 -4
  54. package/slicing/criterion/parse.js +3 -3
  55. package/slicing/static/static-slicer.d.ts +1 -1
  56. package/slicing/static/static-slicer.js +10 -4
  57. package/statistics/statistics.js +1 -1
  58. package/util/json.d.ts +1 -1
  59. package/util/json.js +3 -3
  60. package/util/logic.d.ts +5 -1
  61. package/util/version.js +1 -1
  62. package/abstract-interpretation/domain.d.ts +0 -57
  63. package/abstract-interpretation/domain.js +0 -176
  64. package/abstract-interpretation/handler/binop/binop.d.ts +0 -15
  65. package/abstract-interpretation/handler/binop/binop.js +0 -42
  66. package/abstract-interpretation/handler/binop/operators.d.ts +0 -2
  67. package/abstract-interpretation/handler/binop/operators.js +0 -28
  68. package/abstract-interpretation/handler/handler.d.ts +0 -6
  69. package/abstract-interpretation/handler/handler.js +0 -3
  70. package/abstract-interpretation/processor.d.ts +0 -11
  71. package/abstract-interpretation/processor.js +0 -84
@@ -5,6 +5,7 @@ const assert_1 = require("../../util/assert");
5
5
  const diff_1 = require("./diff");
6
6
  const arrays_1 = require("../../util/arrays");
7
7
  const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
8
+ const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id");
8
9
  const environment_1 = require("../environments/environment");
9
10
  const clone_1 = require("../environments/clone");
10
11
  const built_in_1 = require("../environments/built-in");
@@ -42,8 +43,8 @@ function extractEdgeIds(from, to) {
42
43
  class DataflowGraph {
43
44
  static DEFAULT_ENVIRONMENT = undefined;
44
45
  _idMap;
45
- // this should be linked separately
46
- functionCache = new Map();
46
+ /* Set of vertices which have sideEffects that we do not know anything about */
47
+ _unknownSideEffects = new Set();
47
48
  constructor(idMap) {
48
49
  DataflowGraph.DEFAULT_ENVIRONMENT ??= (0, environment_1.initializeCleanEnvironments)();
49
50
  this._idMap = idMap;
@@ -96,16 +97,22 @@ class DataflowGraph {
96
97
  get idMap() {
97
98
  return this._idMap;
98
99
  }
100
+ /**
101
+ * Retrieves the set of vertices which have side effects that we do not know anything about.
102
+ */
103
+ get unknownSideEffects() {
104
+ return this._unknownSideEffects;
105
+ }
99
106
  /** Allows setting the id-map explicitly (which should only be used when, e.g., you plan to compare two dataflow graphs on the same AST-basis) */
100
107
  setIdMap(idMap) {
101
108
  this._idMap = idMap;
102
109
  }
103
110
  /**
104
- * @param includeDefinedFunctions - If true this will iterate over function definitions as well and not just the toplevel
105
- * @returns the ids of all toplevel vertices in the graph together with their vertex information
111
+ * @param includeDefinedFunctions - If true this will iterate over function definitions as well and not just the toplevel
112
+ * @returns the ids of all toplevel vertices in the graph together with their vertex information
106
113
  *
107
114
  * @see #edges
108
- */
115
+ */
109
116
  *vertices(includeDefinedFunctions) {
110
117
  if (includeDefinedFunctions) {
111
118
  yield* this.vertexInformation.entries();
@@ -143,15 +150,15 @@ class DataflowGraph {
143
150
  return this.rootVertices;
144
151
  }
145
152
  /**
146
- * Adds a new vertex to the graph, for ease of use, some arguments are optional and filled automatically.
147
- *
153
+ * Adds a new vertex to the graph, for ease of use, some arguments are optional and filled automatically.
154
+ *
148
155
  * @param vertex - The vertex to add
149
156
  * @param asRoot - If false, this will only add the vertex but do not add it to the {@link rootIds|root vertices} of the graph.
150
157
  * This is probably only of use, when you construct dataflow graphs for tests.
151
158
  *
152
- * @see DataflowGraphVertexInfo
153
- * @see DataflowGraphVertexArgument
154
- */
159
+ * @see DataflowGraphVertexInfo
160
+ * @see DataflowGraphVertexArgument
161
+ */
155
162
  addVertex(vertex, asRoot = true) {
156
163
  const oldVertex = this.vertexInformation.get(vertex.id);
157
164
  if (oldVertex !== undefined) {
@@ -170,11 +177,11 @@ class DataflowGraph {
170
177
  return this;
171
178
  }
172
179
  /**
173
- * Will insert a new edge into the graph,
174
- * if the direction of the edge is of no importance (`same-read-read` or `same-def-def`), source
175
- * and target will be sorted so that `from` has the lower, and `to` the higher id (default ordering).
176
- * Please note, that this will never make edges to {@link BuiltIn} as they are not part of the graph.
177
- */
180
+ * Will insert a new edge into the graph,
181
+ * if the direction of the edge is of no importance (`same-read-read` or `same-def-def`), source
182
+ * and target will be sorted so that `from` has the lower, and `to` the higher id (default ordering).
183
+ * Please note that this will never make edges to {@link BuiltIn} as they are not part of the graph.
184
+ */
178
185
  addEdge(from, to, edgeInfo) {
179
186
  const { fromId, toId } = extractEdgeIds(from, to);
180
187
  const { type, ...rest } = edgeInfo;
@@ -232,6 +239,9 @@ class DataflowGraph {
232
239
  this.rootVertices.add(root);
233
240
  }
234
241
  }
242
+ for (const unknown of otherGraph.unknownSideEffects) {
243
+ this._unknownSideEffects.add(unknown);
244
+ }
235
245
  for (const [id, info] of otherGraph.vertexInformation) {
236
246
  const currentInfo = this.vertexInformation.get(id);
237
247
  this.vertexInformation.set(id, currentInfo === undefined ? info : mergeNodeInfos(currentInfo, info));
@@ -258,15 +268,6 @@ class DataflowGraph {
258
268
  }
259
269
  }
260
270
  }
261
- equals(other, diff = false, names = { left: 'left', right: 'right' }) {
262
- const report = (0, diff_1.diffOfDataflowGraphs)({ name: names.left, graph: this }, { name: names.right, graph: other });
263
- if (diff) {
264
- return report;
265
- }
266
- else {
267
- return report.isEqual();
268
- }
269
- }
270
271
  /**
271
272
  * Marks a vertex in the graph to be a definition
272
273
  * @param reference - The reference to the vertex to mark as definition
@@ -281,6 +282,32 @@ class DataflowGraph {
281
282
  this.vertexInformation.set(reference.nodeId, { ...vertex, tag: 'variable-definition' });
282
283
  }
283
284
  }
285
+ /**
286
+ * Marks a vertex in the graph to be a function call with the new information
287
+ * @param info - The information about the new function call node
288
+ */
289
+ updateToFunctionCall(info) {
290
+ const vertex = this.getVertex(info.id, true);
291
+ (0, assert_1.guard)(vertex !== undefined, () => `node must be defined for ${JSON.stringify(info.id)} to update it to a function call`);
292
+ (0, assert_1.guard)(vertex.tag === "use" /* VertexType.Use */, () => `node must be a use node for ${JSON.stringify(info.id)} to update it to a function call`);
293
+ this.vertexInformation.set(info.id, { ...vertex, ...info, tag: "function-call" /* VertexType.FunctionCall */ });
294
+ }
295
+ /** If you do not pass the `to` node, this will just mark the node as maybe */
296
+ addControlDependency(from, to, when) {
297
+ to = to ? (0, node_id_1.normalizeIdToNumberIfPossible)(to) : undefined;
298
+ const vertex = this.getVertex(from, true);
299
+ (0, assert_1.guard)(vertex !== undefined, () => `node must be defined for ${from} to add control dependency`);
300
+ vertex.controlDependencies ??= [];
301
+ if (to && vertex.controlDependencies.every(({ id, when: cond }) => id !== to && when !== cond)) {
302
+ vertex.controlDependencies.push({ id: to, when });
303
+ }
304
+ return this;
305
+ }
306
+ /** Marks the given node as having unknown side effects */
307
+ markIdForUnknownSideEffects(id) {
308
+ this._unknownSideEffects.add((0, node_id_1.normalizeIdToNumberIfPossible)(id));
309
+ return this;
310
+ }
284
311
  }
285
312
  exports.DataflowGraph = DataflowGraph;
286
313
  function mergeNodeInfos(current, next) {
@@ -79,7 +79,7 @@ export interface DataflowGraphVertexFunctionDefinition extends DataflowGraphVert
79
79
  */
80
80
  subflow: DataflowFunctionFlowInformation;
81
81
  /**
82
- * All exist points of the function definitions.
82
+ * All exit points of the function definitions.
83
83
  * In other words: last expressions/return calls
84
84
  */
85
85
  exitPoints: readonly NodeId[];
@@ -87,4 +87,9 @@ export interface DataflowGraphVertexFunctionDefinition extends DataflowGraphVert
87
87
  }
88
88
  export type DataflowGraphVertexArgument = DataflowGraphVertexUse | DataflowGraphVertexVariableDefinition | DataflowGraphVertexFunctionDefinition | DataflowGraphVertexFunctionCall | DataflowGraphValue;
89
89
  export type DataflowGraphVertexInfo = Required<DataflowGraphVertexArgument>;
90
+ export declare function isValueVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphValue;
91
+ export declare function isUseVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexUse;
92
+ export declare function isFunctionCallVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexFunctionCall;
93
+ export declare function isVariableDefinitionVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexVariableDefinition;
94
+ export declare function isFunctionDefinitionVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexFunctionDefinition;
90
95
  export {};
@@ -1,3 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isFunctionDefinitionVertex = exports.isVariableDefinitionVertex = exports.isFunctionCallVertex = exports.isUseVertex = exports.isValueVertex = void 0;
4
+ function isValueVertex(vertex) {
5
+ return vertex.tag === "value" /* VertexType.Value */;
6
+ }
7
+ exports.isValueVertex = isValueVertex;
8
+ function isUseVertex(vertex) {
9
+ return vertex.tag === "use" /* VertexType.Use */;
10
+ }
11
+ exports.isUseVertex = isUseVertex;
12
+ function isFunctionCallVertex(vertex) {
13
+ return vertex.tag === "function-call" /* VertexType.FunctionCall */;
14
+ }
15
+ exports.isFunctionCallVertex = isFunctionCallVertex;
16
+ function isVariableDefinitionVertex(vertex) {
17
+ return vertex.tag === "variable-definition" /* VertexType.VariableDefinition */;
18
+ }
19
+ exports.isVariableDefinitionVertex = isVariableDefinitionVertex;
20
+ function isFunctionDefinitionVertex(vertex) {
21
+ return vertex.tag === "function-definition" /* VertexType.FunctionDefinition */;
22
+ }
23
+ exports.isFunctionDefinitionVertex = isFunctionDefinitionVertex;
3
24
  //# sourceMappingURL=vertex.js.map
@@ -57,7 +57,7 @@ export interface DataflowInformation extends DataflowCfgInformation {
57
57
  graph: DataflowGraph;
58
58
  }
59
59
  export declare function initializeCleanDataflowInformation<T>(entryPoint: NodeId, data: Pick<DataflowProcessorInformation<T>, 'environment' | 'completeAst'>): DataflowInformation;
60
- export declare function happensInEveryBranch(controlDependencies: ControlDependency[] | undefined): boolean;
60
+ export declare function happensInEveryBranch(controlDependencies: readonly ControlDependency[] | undefined): boolean;
61
61
  export declare function alwaysExits(data: DataflowInformation): boolean;
62
62
  export declare function filterOutLoopExitPoints(exitPoints: readonly ExitPoint[]): readonly ExitPoint[];
63
63
  export declare function diffControlDependency<Report extends WriteableDifferenceReport>(a: ControlDependency | undefined, b: ControlDependency | undefined, info: GenericDifferenceInformation<Report>): void;
@@ -4,6 +4,7 @@ import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/
4
4
  import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
5
5
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
6
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
+ import type { ForceArguments } from '../common';
7
8
  export declare function processAccess<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: {
8
9
  treatIndicesAsString: boolean;
9
- }): DataflowInformation;
10
+ } & ForceArguments): DataflowInformation;
@@ -6,16 +6,41 @@ const known_call_handling_1 = require("../known-call-handling");
6
6
  const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
7
7
  const logger_1 = require("../../../../../logger");
8
8
  const environment_1 = require("../../../../../environments/environment");
9
+ const built_in_1 = require("../../../../../environments/built-in");
10
+ const built_in_assignment_1 = require("./built-in-assignment");
11
+ function tableAssignmentProcessor(name, args, rootId, data, outInfo) {
12
+ outInfo.definitionRootNodes.push(rootId);
13
+ return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
14
+ }
9
15
  function processAccess(name, args, rootId, data, config) {
10
16
  if (args.length < 2) {
11
17
  logger_1.dataflowLogger.warn(`Access ${name.content} has less than 2 arguments, skipping`);
12
- return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
18
+ return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs }).information;
13
19
  }
14
20
  const head = args[0];
15
21
  (0, assert_1.guard)(head !== r_function_call_1.EmptyArgument, () => `Access ${name.content} has no source, impossible!`);
16
22
  let fnCall;
17
23
  if (!config.treatIndicesAsString) {
18
- fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data });
24
+ /* within an access operation which treats its fields, we redefine the table assignment ':=' as a trigger if this is to be treated as a definition */
25
+ // do we have a local definition that needs to be recovered?
26
+ const existing = data.environment.current.memory.get(':=');
27
+ const outInfo = { definitionRootNodes: [] };
28
+ data.environment.current.memory.set(':=', [{
29
+ kind: 'built-in-function',
30
+ definedAt: built_in_1.BuiltIn,
31
+ controlDependencies: undefined,
32
+ processor: (name, args, rootId, data) => tableAssignmentProcessor(name, args, rootId, data, outInfo),
33
+ name: ':=',
34
+ nodeId: built_in_1.BuiltIn
35
+ }]);
36
+ fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs });
37
+ /* recover the environment */
38
+ if (existing !== undefined) {
39
+ data.environment.current.memory.set(':=', existing);
40
+ }
41
+ if (head.value && outInfo.definitionRootNodes.length > 0) {
42
+ (0, built_in_assignment_1.markAsAssignment)(fnCall.information, { kind: 'variable', name: head.value.lexeme ?? '', nodeId: head.value.info.id, definedAt: rootId, controlDependencies: [] }, outInfo.definitionRootNodes, rootId);
43
+ }
19
44
  }
20
45
  else {
21
46
  const newArgs = [...args];
@@ -38,7 +63,7 @@ function processAccess(name, args, rootId, data, config) {
38
63
  };
39
64
  }
40
65
  }
41
- fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args: newArgs, rootId, data });
66
+ fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args: newArgs, rootId, data, forceArgs: config.forceArgs });
42
67
  }
43
68
  const info = fnCall.information;
44
69
  info.graph.addEdge(name.info.id, fnCall.processedArguments[0]?.entryPoint ?? head.info.id, { type: 8 /* EdgeType.Returns */ });
@@ -47,10 +72,7 @@ function processAccess(name, args, rootId, data, config) {
47
72
  if (arg !== undefined) {
48
73
  info.graph.addEdge(name.info.id, arg.entryPoint, { type: 1 /* EdgeType.Reads */ });
49
74
  }
50
- if (config.treatIndicesAsString) {
51
- // everything but the first is disabled here
52
- break;
53
- }
75
+ /* we include the read edges to the constant arguments as well so that they are included if necessary */
54
76
  }
55
77
  return {
56
78
  ...info,
@@ -0,0 +1,14 @@
1
+ import type { DataflowProcessorInformation } from '../../../../../processor';
2
+ import type { DataflowInformation } from '../../../../../info';
3
+ import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
4
+ import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
5
+ import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
+ import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
+ import type { MergeableRecord } from '../../../../../../util/objects';
8
+ export interface BuiltInApplyConfiguration extends MergeableRecord {
9
+ /** the 0-based index of the argument which is the actual function passed, defaults to 1 */
10
+ readonly indexOfFunction?: number;
11
+ /** does the argument have a name that it can be given by as well? */
12
+ readonly nameOfFunctionArgument?: string;
13
+ }
14
+ export declare function processApply<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, { indexOfFunction, nameOfFunctionArgument }: BuiltInApplyConfiguration): DataflowInformation;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processApply = void 0;
4
+ const known_call_handling_1 = require("../known-call-handling");
5
+ const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
6
+ const logger_1 = require("../../../../../logger");
7
+ function processApply(name, args, rootId, data, { indexOfFunction = 1, nameOfFunctionArgument }) {
8
+ /* as the length is one-based and the argument filter mapping is zero-based, we do not have to subtract 1 */
9
+ const forceArgsMask = new Array(indexOfFunction).fill(false);
10
+ forceArgsMask.push(true);
11
+ const { information, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({
12
+ name, args, rootId, data, forceArgs: forceArgsMask
13
+ });
14
+ let index = indexOfFunction;
15
+ /* search, if one of the arguments actually contains the argument name if given in the config */
16
+ if (nameOfFunctionArgument !== undefined) {
17
+ const mayFn = args.findIndex(arg => arg !== r_function_call_1.EmptyArgument && arg.name && arg.name.content === nameOfFunctionArgument);
18
+ if (mayFn >= 0) {
19
+ index = mayFn;
20
+ }
21
+ }
22
+ /* validate, that we indeed have so many arguments to fill this one :D */
23
+ if (index >= args.length) {
24
+ logger_1.dataflowLogger.warn(`Function argument at index ${index} not found, skipping`);
25
+ return information;
26
+ }
27
+ const arg = args[index];
28
+ if (arg === r_function_call_1.EmptyArgument || arg?.value?.type !== "RSymbol" /* RType.Symbol */) {
29
+ logger_1.dataflowLogger.warn(`Expected symbol as argument at index ${index}, but got ${JSON.stringify(arg)} instead.`);
30
+ return information;
31
+ }
32
+ const functionSymbol = arg.value;
33
+ const allOtherArguments = processedArguments.filter((_, i) => i !== index).map((arg, i) => {
34
+ const counterpart = args[i];
35
+ if (arg && counterpart !== r_function_call_1.EmptyArgument && counterpart.name) {
36
+ return {
37
+ name: counterpart.name.content,
38
+ controlDependencies: data.controlDependencies,
39
+ nodeId: arg.entryPoint
40
+ };
41
+ }
42
+ else {
43
+ return r_function_call_1.EmptyArgument;
44
+ }
45
+ });
46
+ const applyCallId = functionSymbol.info.id;
47
+ /* identify it as a full-blown function call :) */
48
+ information.graph.updateToFunctionCall({
49
+ tag: "function-call" /* VertexType.FunctionCall */,
50
+ id: applyCallId,
51
+ name: functionSymbol.content,
52
+ args: allOtherArguments,
53
+ environment: data.environment,
54
+ onlyBuiltin: false,
55
+ controlDependencies: data.controlDependencies
56
+ });
57
+ for (const arg of processedArguments) {
58
+ if (arg) {
59
+ information.graph.addEdge(applyCallId, arg.entryPoint, { type: 64 /* EdgeType.Argument */ });
60
+ }
61
+ }
62
+ return information;
63
+ }
64
+ exports.processApply = processApply;
65
+ //# sourceMappingURL=built-in-apply.js.map
@@ -5,7 +5,11 @@ import type { RNode } from '../../../../../../r-bridge/lang-4.x/ast/model/model'
5
5
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
6
  import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
7
7
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
8
- export interface AssignmentConfiguration {
8
+ import type { IdentifierDefinition } from '../../../../../environments/identifier';
9
+ import type { ForceArguments } from '../common';
10
+ import type { REnvironmentInformation } from '../../../../../environments/environment';
11
+ import type { DataflowGraph } from '../../../../../graph/graph';
12
+ export interface AssignmentConfiguration extends ForceArguments {
9
13
  readonly superAssignment?: boolean;
10
14
  readonly swapSourceAndTarget?: boolean;
11
15
  readonly makeMaybe?: boolean;
@@ -19,7 +23,7 @@ export interface AssignmentConfiguration {
19
23
  */
20
24
  export declare function processAssignment<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: AssignmentConfiguration): DataflowInformation;
21
25
  export interface AssignmentToSymbolParameters<OtherInfo> extends AssignmentConfiguration {
22
- readonly name: RSymbol<OtherInfo & ParentInformation>;
26
+ readonly nameOfAssignmentFunction: string;
23
27
  readonly source: RNode<OtherInfo & ParentInformation>;
24
28
  readonly args: [DataflowInformation, DataflowInformation];
25
29
  readonly target: RSymbol<OtherInfo & ParentInformation>;
@@ -27,3 +31,16 @@ export interface AssignmentToSymbolParameters<OtherInfo> extends AssignmentConfi
27
31
  readonly data: DataflowProcessorInformation<OtherInfo>;
28
32
  readonly information: DataflowInformation;
29
33
  }
34
+ /**
35
+ * Consider a call like `x <- v`
36
+ * @param information - the information to define the assignment within
37
+ * @param nodeToDefine - `x`
38
+ * @param sourceIds - `v`
39
+ * @param rootIdOfAssignment - `<-`
40
+ * @param quoteSource - whether to quote the source (i.e., define `x` without a direct reference to `v`)
41
+ * @param superAssignment - whether this is a super assignment (i.e., `<<-`)
42
+ */
43
+ export declare function markAsAssignment(information: {
44
+ environment: REnvironmentInformation;
45
+ graph: DataflowGraph;
46
+ }, nodeToDefine: IdentifierDefinition, sourceIds: readonly NodeId[], rootIdOfAssignment: NodeId, quoteSource?: boolean, superAssignment?: boolean): void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.processAssignment = void 0;
3
+ exports.markAsAssignment = exports.processAssignment = void 0;
4
4
  const known_call_handling_1 = require("../known-call-handling");
5
5
  const assert_1 = require("../../../../../../util/assert");
6
6
  const log_1 = require("../../../../../../util/log");
@@ -15,7 +15,7 @@ function toReplacementSymbol(target, prefix, superAssignment) {
15
15
  return {
16
16
  type: "RSymbol" /* RType.Symbol */,
17
17
  info: target.info,
18
- /* they are all mapped to <- in R, but we mark super as well */
18
+ /* they are all mapped to `<-` in R, but we mark super as well */
19
19
  content: `${prefix}${superAssignment ? '<<-' : '<-'}`,
20
20
  lexeme: target.lexeme,
21
21
  location: target.location,
@@ -35,16 +35,16 @@ function processAssignment(name,
35
35
  args, rootId, data, config) {
36
36
  if (args.length != 2) {
37
37
  logger_1.dataflowLogger.warn(`Assignment ${name.content} has something else than 2 arguments, skipping`);
38
- return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
38
+ return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs }).information;
39
39
  }
40
40
  const effectiveArgs = getEffectiveOrder(config, args);
41
41
  const { target, source } = extractSourceAndTarget(effectiveArgs, name);
42
42
  const { type, named } = target;
43
43
  if (type === "RSymbol" /* RType.Symbol */) {
44
- const res = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, reverseOrder: !config.swapSourceAndTarget });
44
+ const res = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, reverseOrder: !config.swapSourceAndTarget, forceArgs: config.forceArgs });
45
45
  return processAssignmentToSymbol({
46
46
  ...config,
47
- name,
47
+ nameOfAssignmentFunction: name.content,
48
48
  source,
49
49
  target,
50
50
  args: getEffectiveOrder(config, res.processedArguments),
@@ -68,7 +68,7 @@ args, rootId, data, config) {
68
68
  return processAssignmentToString(target, args, name, rootId, data, config, source);
69
69
  }
70
70
  logger_1.dataflowLogger.warn(`Assignment ${name.content} has an unknown target type ${target.type}, skipping`);
71
- return (0, known_call_handling_1.processKnownFunctionCall)({ name, args: effectiveArgs, rootId, data }).information;
71
+ return (0, known_call_handling_1.processKnownFunctionCall)({ name, args: effectiveArgs, rootId, data, forceArgs: config.forceArgs }).information;
72
72
  }
73
73
  exports.processAssignment = processAssignment;
74
74
  function extractSourceAndTarget(args, name) {
@@ -105,11 +105,12 @@ function processAssignmentToString(target, args, name, rootId, data, config, sou
105
105
  args: mappedArgs,
106
106
  rootId,
107
107
  data,
108
- reverseOrder: !config.swapSourceAndTarget
108
+ reverseOrder: !config.swapSourceAndTarget,
109
+ forceArgs: config.forceArgs
109
110
  });
110
111
  return processAssignmentToSymbol({
111
112
  ...config,
112
- name,
113
+ nameOfAssignmentFunction: name.content,
113
114
  source,
114
115
  target: symbol,
115
116
  args: getEffectiveOrder(config, res.processedArguments),
@@ -121,10 +122,38 @@ function processAssignmentToString(target, args, name, rootId, data, config, sou
121
122
  function checkFunctionDef(source, sourceInfo) {
122
123
  return sourceInfo.graph.getVertex(source.info.id)?.tag === "function-definition" /* VertexType.FunctionDefinition */;
123
124
  }
125
+ /**
126
+ * Consider a call like `x <- v`
127
+ * @param information - the information to define the assignment within
128
+ * @param nodeToDefine - `x`
129
+ * @param sourceIds - `v`
130
+ * @param rootIdOfAssignment - `<-`
131
+ * @param quoteSource - whether to quote the source (i.e., define `x` without a direct reference to `v`)
132
+ * @param superAssignment - whether this is a super assignment (i.e., `<<-`)
133
+ */
134
+ function markAsAssignment(information, nodeToDefine, sourceIds, rootIdOfAssignment, quoteSource, superAssignment) {
135
+ information.environment = (0, define_1.define)(nodeToDefine, superAssignment, information.environment);
136
+ information.graph.setDefinitionOfVertex(nodeToDefine);
137
+ if (!quoteSource) {
138
+ for (const sourceId of sourceIds) {
139
+ information.graph.addEdge(nodeToDefine, sourceId, { type: 2 /* EdgeType.DefinedBy */ });
140
+ }
141
+ }
142
+ information.graph.addEdge(nodeToDefine, rootIdOfAssignment, { type: 2 /* EdgeType.DefinedBy */ });
143
+ // kinda dirty, but we have to remove existing read edges for the symbol, added by the child
144
+ const out = information.graph.outgoingEdges(nodeToDefine.nodeId);
145
+ for (const [id, edge] of (out ?? [])) {
146
+ edge.types &= ~1 /* EdgeType.Reads */;
147
+ if (edge.types == 0) {
148
+ out?.delete(id);
149
+ }
150
+ }
151
+ }
152
+ exports.markAsAssignment = markAsAssignment;
124
153
  /**
125
154
  * Helper function whenever it is known that the _target_ of an assignment is a (single) symbol (i.e. `x <- ...`, but not `names(x) <- ...`).
126
155
  */
127
- function processAssignmentToSymbol({ name, source, args: [targetArg, sourceArg], target, rootId, data, information, superAssignment, makeMaybe, quoteSource }) {
156
+ function processAssignmentToSymbol({ nameOfAssignmentFunction, source, args: [targetArg, sourceArg], target, rootId, data, information, superAssignment, makeMaybe, quoteSource }) {
128
157
  const isFunctionDef = checkFunctionDef(source, sourceArg);
129
158
  const writeNodes = produceWrittenNodes(rootId, targetArg, isFunctionDef, data, makeMaybe ?? false);
130
159
  if (writeNodes.length !== 1 && log_1.log.settings.minLevel <= 4 /* LogLevel.Warn */) {
@@ -132,25 +161,12 @@ function processAssignmentToSymbol({ name, source, args: [targetArg, sourceArg],
132
161
  }
133
162
  // we drop the first arg which we use to pass along arguments :D
134
163
  const readFromSourceWritten = sourceArg.out.slice(1);
135
- const readTargets = [{ nodeId: rootId, name: name.content, controlDependencies: data.controlDependencies }, ...sourceArg.unknownReferences, ...sourceArg.in, ...targetArg.in.filter(i => i.nodeId !== target.info.id), ...readFromSourceWritten];
164
+ const readTargets = [{ nodeId: rootId, name: nameOfAssignmentFunction, controlDependencies: data.controlDependencies }, ...sourceArg.unknownReferences, ...sourceArg.in, ...targetArg.in.filter(i => i.nodeId !== target.info.id), ...readFromSourceWritten];
136
165
  const writeTargets = [...writeNodes, ...writeNodes, ...readFromSourceWritten];
137
166
  information.environment = (0, overwrite_1.overwriteEnvironment)(targetArg.environment, sourceArg.environment);
138
167
  // install assigned variables in environment
139
168
  for (const write of writeNodes) {
140
- information.environment = (0, define_1.define)(write, superAssignment, information.environment);
141
- information.graph.setDefinitionOfVertex(write);
142
- if (!quoteSource) {
143
- information.graph.addEdge(write, source.info.id, { type: 2 /* EdgeType.DefinedBy */ });
144
- }
145
- information.graph.addEdge(write, rootId, { type: 2 /* EdgeType.DefinedBy */ });
146
- // kinda dirty, but we have to remove existing read edges for the symbol, added by the child
147
- const out = information.graph.outgoingEdges(write.nodeId);
148
- for (const [id, edge] of (out ?? [])) {
149
- edge.types &= ~1 /* EdgeType.Reads */;
150
- if (edge.types == 0) {
151
- out?.delete(id);
152
- }
153
- }
169
+ markAsAssignment(information, write, [source.info.id], rootId, quoteSource, superAssignment);
154
170
  }
155
171
  information.graph.addEdge(rootId, targetArg.entryPoint, { type: 8 /* EdgeType.Returns */ });
156
172
  if (quoteSource) {
@@ -63,7 +63,7 @@ function processForLoop(name, args, rootId, data) {
63
63
  for (const readId of readIdsToLink) {
64
64
  nextGraph.addEdge(readId.nodeId, write.nodeId, { type: 1 /* EdgeType.Reads */ });
65
65
  }
66
- // now, we remove the name from the id shares as they are no longer needed
66
+ // now, we remove the name from the id shares as they are no longer necessary
67
67
  nameIdShares.delete(name);
68
68
  nextGraph.setDefinitionOfVertex(write);
69
69
  }
@@ -45,8 +45,17 @@ function processFunctionDefinition(name, args, rootId, data) {
45
45
  const readInBody = [...body.in, ...body.unknownReferences];
46
46
  // there is no uncertainty regarding the arguments, as if a function header is executed, so is its body
47
47
  const remainingRead = (0, linker_1.linkInputs)(readInBody, paramsEnvironments, readInParameters.slice(), body.graph, true /* functions do not have to be called */);
48
+ // functions can be called multiple times,
49
+ // so if they have a global effect, we have to link them as if they would be executed a loop
50
+ /* theoretically, we should just check if there is a global effect-write somewhere within */
51
+ if (remainingRead.length > 0) {
52
+ const nameIdShares = (0, linker_1.produceNameSharedIdMap)(remainingRead);
53
+ const definedInLocalEnvironment = new Set([...bodyEnvironment.current.memory.values()].flat().map(d => d.nodeId));
54
+ // Everything that is in body.out but not within the local environment populated for the function scope is a potential escape ~> global definition
55
+ const globalBodyOut = body.out.filter(d => !definedInLocalEnvironment.has(d.nodeId));
56
+ (0, linker_1.linkCircularRedefinitionsWithinALoop)(body.graph, nameIdShares, globalBodyOut);
57
+ }
48
58
  subgraph.mergeWith(body.graph);
49
- logger_1.dataflowLogger.trace(`Function definition with id ${name.info.id} has ${remainingRead.length} remaining reads`);
50
59
  const outEnvironment = (0, overwrite_1.overwriteEnvironment)(paramsEnvironments, bodyEnvironment);
51
60
  for (const read of remainingRead) {
52
61
  if (read.name) {
@@ -33,23 +33,23 @@ function processIfThenElse(name, args, rootId, data) {
33
33
  // we should defer this to the abstract interpretation
34
34
  const conditionIsFalse = (0, resolve_by_name_1.resolvesToBuiltInConstant)(condArg?.lexeme, data.environment, false);
35
35
  const conditionIsTrue = (0, resolve_by_name_1.resolvesToBuiltInConstant)(condArg?.lexeme, data.environment, true);
36
- if (conditionIsFalse !== 'always') {
36
+ if (conditionIsFalse !== 0 /* Ternary.Always */) {
37
37
  then = (0, processor_1.processDataflowFor)(thenArg, data);
38
38
  if (then.entryPoint) {
39
39
  then.graph.addEdge(name.info.id, then.entryPoint, { type: 8 /* EdgeType.Returns */ });
40
40
  }
41
- if (conditionIsTrue !== 'always') {
41
+ if (conditionIsTrue !== 0 /* Ternary.Always */) {
42
42
  makeThenMaybe = true;
43
43
  }
44
44
  }
45
45
  let otherwise;
46
46
  let makeOtherwiseMaybe = false;
47
- if (otherwiseArg !== undefined && conditionIsTrue !== 'always') {
47
+ if (otherwiseArg !== undefined && conditionIsTrue !== 0 /* Ternary.Always */) {
48
48
  otherwise = (0, processor_1.processDataflowFor)(otherwiseArg, data);
49
49
  if (otherwise.entryPoint) {
50
50
  otherwise.graph.addEdge(name.info.id, otherwise.entryPoint, { type: 8 /* EdgeType.Returns */ });
51
51
  }
52
- if (conditionIsFalse !== 'always') {
52
+ if (conditionIsFalse !== 0 /* Ternary.Always */) {
53
53
  makeOtherwiseMaybe = true;
54
54
  }
55
55
  }
@@ -6,14 +6,15 @@ const logger_1 = require("../../../../../logger");
6
6
  const unpack_argument_1 = require("../argument/unpack-argument");
7
7
  const make_argument_1 = require("../argument/make-argument");
8
8
  function processLibrary(name, args, rootId, data) {
9
+ /* we do not really know what loading the library does and what side effects it causes, hence we mark it as an unknown side effect */
9
10
  if (args.length !== 1) {
10
11
  logger_1.dataflowLogger.warn(`Currently only one-arg library-likes are allows (for ${name.content}), skipping`);
11
- return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
12
+ return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, hasUnknownSideEffect: true }).information;
12
13
  }
13
14
  const nameToLoad = (0, unpack_argument_1.unpackArgument)(args[0]);
14
15
  if (nameToLoad === undefined || nameToLoad.type !== "RSymbol" /* RType.Symbol */) {
15
16
  logger_1.dataflowLogger.warn('No library name provided, skipping');
16
- return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
17
+ return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, hasUnknownSideEffect: true }).information;
17
18
  }
18
19
  // treat as a function call but convert the first argument to a string
19
20
  const newArg = {
@@ -26,7 +27,10 @@ function processLibrary(name, args, rootId, data) {
26
27
  str: nameToLoad.content
27
28
  }
28
29
  };
29
- return (0, known_call_handling_1.processKnownFunctionCall)({ name, args: (0, make_argument_1.wrapArgumentsUnnamed)([newArg], data.completeAst.idMap), rootId, data }).information;
30
+ return (0, known_call_handling_1.processKnownFunctionCall)({
31
+ name, args: (0, make_argument_1.wrapArgumentsUnnamed)([newArg], data.completeAst.idMap), rootId, data,
32
+ hasUnknownSideEffect: true
33
+ }).information;
30
34
  }
31
35
  exports.processLibrary = processLibrary;
32
36
  //# sourceMappingURL=built-in-library.js.map
@@ -4,6 +4,7 @@ import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/
4
4
  import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
5
5
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
6
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
- export declare function processQuote<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config?: {
7
+ import type { ForceArguments } from '../common';
8
+ export declare function processQuote<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: {
8
9
  quoteArgumentsWithIndex?: number;
9
- }): DataflowInformation;
10
+ } & ForceArguments): DataflowInformation;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.processQuote = void 0;
4
4
  const known_call_handling_1 = require("../known-call-handling");
5
5
  function processQuote(name, args, rootId, data, config) {
6
- const { information, processedArguments, fnRef } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data });
6
+ const { information, processedArguments, fnRef } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs });
7
7
  const inRefs = [fnRef];
8
8
  const outRefs = [];
9
9
  const unknownRefs = [];