@eagleoutice/flowr 2.1.8 → 2.1.10

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 (85) hide show
  1. package/README.md +3 -0
  2. package/benchmark/summarizer/first-phase/process.js +6 -5
  3. package/cli/repl/commands/repl-dataflow.js +5 -2
  4. package/cli/repl/commands/repl-normalize.js +5 -2
  5. package/cli/repl/commands/repl-query.js +2 -2
  6. package/cli/repl/server/messages/message-query.js +1 -1
  7. package/config.d.ts +21 -0
  8. package/config.js +19 -2
  9. package/dataflow/environments/built-in.d.ts +2 -0
  10. package/dataflow/environments/built-in.js +2 -0
  11. package/dataflow/environments/default-builtin-config.js +48 -8
  12. package/dataflow/environments/define.js +78 -0
  13. package/dataflow/environments/environment.d.ts +46 -8
  14. package/dataflow/environments/environment.js +24 -1
  15. package/dataflow/environments/identifier.d.ts +60 -10
  16. package/dataflow/environments/identifier.js +11 -2
  17. package/dataflow/environments/resolve-by-name.d.ts +10 -5
  18. package/dataflow/environments/resolve-by-name.js +103 -5
  19. package/dataflow/extractor.js +5 -4
  20. package/dataflow/graph/dataflowgraph-builder.d.ts +6 -0
  21. package/dataflow/graph/dataflowgraph-builder.js +8 -0
  22. package/dataflow/graph/edge.d.ts +10 -4
  23. package/dataflow/graph/edge.js +12 -5
  24. package/dataflow/graph/graph.d.ts +41 -3
  25. package/dataflow/graph/graph.js +39 -34
  26. package/dataflow/graph/vertex.d.ts +122 -8
  27. package/dataflow/graph/vertex.js +19 -0
  28. package/dataflow/info.d.ts +79 -11
  29. package/dataflow/info.js +20 -0
  30. package/dataflow/internal/linker.d.ts +4 -2
  31. package/dataflow/internal/linker.js +12 -5
  32. package/dataflow/internal/process/functions/call/built-in/built-in-access.d.ts +11 -0
  33. package/dataflow/internal/process/functions/call/built-in/built-in-access.js +141 -49
  34. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +8 -3
  35. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +40 -11
  36. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.d.ts +16 -0
  37. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +83 -6
  38. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +3 -3
  39. package/dataflow/internal/process/functions/call/built-in/built-in-list.d.ts +15 -0
  40. package/dataflow/internal/process/functions/call/built-in/built-in-list.js +50 -0
  41. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.d.ts +1 -1
  42. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +29 -1
  43. package/dataflow/internal/process/functions/call/common.js +16 -2
  44. package/dataflow/internal/process/functions/call/known-call-handling.d.ts +2 -1
  45. package/dataflow/internal/process/functions/call/known-call-handling.js +3 -2
  46. package/documentation/doc-util/doc-dfg.d.ts +0 -1
  47. package/documentation/doc-util/doc-dfg.js +1 -14
  48. package/documentation/print-capabilities-markdown.js +1 -1
  49. package/documentation/print-dataflow-graph-wiki.js +26 -7
  50. package/documentation/print-interface-wiki.js +6 -1
  51. package/documentation/print-linting-and-testing-wiki.js +60 -26
  52. package/documentation/print-query-wiki.js +1 -1
  53. package/package.json +17 -3
  54. package/queries/catalog/call-context-query/call-context-query-executor.js +1 -1
  55. package/queries/catalog/call-context-query/call-context-query-format.d.ts +13 -0
  56. package/queries/catalog/call-context-query/call-context-query-format.js +3 -1
  57. package/queries/catalog/call-context-query/cascade-action.d.ts +8 -0
  58. package/queries/catalog/call-context-query/cascade-action.js +13 -0
  59. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.d.ts +11 -1
  60. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +41 -4
  61. package/queries/catalog/dependencies-query/dependencies-query-format.js +4 -0
  62. package/queries/query.d.ts +4 -4
  63. package/queries/query.js +17 -5
  64. package/r-bridge/lang-4.x/ast/model/model.d.ts +3 -0
  65. package/r-bridge/lang-4.x/ast/model/nodes/r-number.d.ts +5 -1
  66. package/r-bridge/lang-4.x/ast/model/processing/node-id.d.ts +6 -1
  67. package/r-bridge/lang-4.x/ast/model/processing/node-id.js +6 -1
  68. package/slicing/static/slice-call.d.ts +7 -2
  69. package/slicing/static/slice-call.js +33 -44
  70. package/slicing/static/static-slicer.d.ts +5 -1
  71. package/slicing/static/static-slicer.js +22 -8
  72. package/slicing/static/visiting-queue.d.ts +4 -4
  73. package/slicing/static/visiting-queue.js +5 -3
  74. package/statistics/output/print-stats.js +2 -1
  75. package/statistics/summarizer/post-process/histogram.js +2 -1
  76. package/statistics/summarizer/post-process/post-process-output.js +2 -1
  77. package/statistics/summarizer/second-phase/process.js +3 -3
  78. package/util/arrays.d.ts +1 -1
  79. package/util/arrays.js +3 -3
  80. package/util/cfg/cfg.js +4 -2
  81. package/util/list-access.d.ts +48 -0
  82. package/util/list-access.js +115 -0
  83. package/util/mermaid/cfg.js +1 -1
  84. package/util/summarizer.js +2 -2
  85. package/util/version.js +1 -1
@@ -11,10 +11,23 @@ const environment_1 = require("../../../../../environments/environment");
11
11
  const built_in_1 = require("../../../../../environments/built-in");
12
12
  const built_in_assignment_1 = require("./built-in-assignment");
13
13
  const identifier_1 = require("../../../../../environments/identifier");
14
+ const vertex_1 = require("../../../../../graph/vertex");
15
+ const list_access_1 = require("../../../../../../util/list-access");
14
16
  function tableAssignmentProcessor(name, args, rootId, data, outInfo) {
15
17
  outInfo.definitionRootNodes.push(rootId);
16
18
  return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
17
19
  }
20
+ /**
21
+ * Processes different types of access operations.
22
+ *
23
+ * Example:
24
+ * ```r
25
+ * a[i]
26
+ * a$foo
27
+ * a[[i]]
28
+ * a@foo
29
+ * ```
30
+ */
18
31
  function processAccess(name, args, rootId, data, config) {
19
32
  if (args.length < 2) {
20
33
  logger_1.dataflowLogger.warn(`Access ${name.content} has less than 2 arguments, skipping`);
@@ -26,47 +39,10 @@ function processAccess(name, args, rootId, data, config) {
26
39
  if (!config.treatIndicesAsString) {
27
40
  /* 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 */
28
41
  // do we have a local definition that needs to be recovered?
29
- const existing = data.environment.current.memory.get(':=');
30
- const outInfo = { definitionRootNodes: [] };
31
- data.environment.current.memory.set(':=', [{
32
- type: identifier_1.ReferenceType.BuiltInFunction,
33
- definedAt: built_in_1.BuiltIn,
34
- controlDependencies: undefined,
35
- processor: (name, args, rootId, data) => tableAssignmentProcessor(name, args, rootId, data, outInfo),
36
- name: ':=',
37
- nodeId: built_in_1.BuiltIn
38
- }]);
39
- fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs });
40
- /* recover the environment */
41
- if (existing !== undefined) {
42
- data.environment.current.memory.set(':=', existing);
43
- }
44
- if (head.value && outInfo.definitionRootNodes.length > 0) {
45
- (0, built_in_assignment_1.markAsAssignment)(fnCall.information, { type: identifier_1.ReferenceType.Variable, name: head.value.lexeme ?? '', nodeId: head.value.info.id, definedAt: rootId, controlDependencies: [] }, outInfo.definitionRootNodes, rootId);
46
- }
42
+ fnCall = processNumberBasedAccess(data, name, args, rootId, config, head);
47
43
  }
48
44
  else {
49
- const newArgs = [...args];
50
- // if the argument is a symbol, we convert it to a string for this perspective
51
- for (let i = 1; i < newArgs.length; i++) {
52
- const arg = newArgs[i];
53
- if (arg !== r_function_call_1.EmptyArgument && arg.value?.type === type_1.RType.Symbol) {
54
- newArgs[i] = {
55
- ...arg,
56
- value: {
57
- type: type_1.RType.String,
58
- info: arg.value.info,
59
- lexeme: arg.value.lexeme,
60
- location: arg.value.location,
61
- content: {
62
- quotes: 'none',
63
- str: arg.value.lexeme
64
- }
65
- }
66
- };
67
- }
68
- }
69
- fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args: newArgs, rootId, data, forceArgs: config.forceArgs });
45
+ fnCall = processStringBasedAccess(args, data, name, rootId, config);
70
46
  }
71
47
  const info = fnCall.information;
72
48
  info.graph.addEdge(name.info.id, fnCall.processedArguments[0]?.entryPoint ?? head.info.id, edge_1.EdgeType.Returns);
@@ -80,16 +56,16 @@ function processAccess(name, args, rootId, data, config) {
80
56
  return {
81
57
  ...info,
82
58
  /*
83
- * Keep active nodes in case of assignments etc.
84
- * We make them maybe as a kind of hack.
85
- * This way when using
86
- * ```ts
87
- * a[[1]] <- 3
88
- * a[[2]] <- 4
89
- * a
90
- * ```
91
- * the read for a will use both accesses as potential definitions and not just the last one!
92
- */
59
+ * Keep active nodes in case of assignments etc.
60
+ * We make them maybe as a kind of hack.
61
+ * This way when using
62
+ * ```ts
63
+ * a[[1]] <- 3
64
+ * a[[2]] <- 4
65
+ * a
66
+ * ```
67
+ * the read for a will use both accesses as potential definitions and not just the last one!
68
+ */
93
69
  unknownReferences: (0, environment_1.makeAllMaybe)(info.unknownReferences, info.graph, info.environment, false),
94
70
  entryPoint: rootId,
95
71
  /** it is, to be precise, the accessed element we want to map to maybe */
@@ -103,4 +79,120 @@ function processAccess(name, args, rootId, data, config) {
103
79
  })
104
80
  };
105
81
  }
82
+ /**
83
+ * Processes different types of number-based access operations.
84
+ *
85
+ * Example:
86
+ * ```r
87
+ * a[i]
88
+ * a[[i]]
89
+ * ```
90
+ */
91
+ function processNumberBasedAccess(data, name, args, rootId, config, head) {
92
+ const existing = data.environment.current.memory.get(':=');
93
+ const outInfo = { definitionRootNodes: [] };
94
+ data.environment.current.memory.set(':=', [{
95
+ type: identifier_1.ReferenceType.BuiltInFunction,
96
+ definedAt: built_in_1.BuiltIn,
97
+ controlDependencies: undefined,
98
+ processor: (name, args, rootId, data) => tableAssignmentProcessor(name, args, rootId, data, outInfo),
99
+ name: ':=',
100
+ nodeId: built_in_1.BuiltIn,
101
+ }]);
102
+ const fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs });
103
+ /* recover the environment */
104
+ if (existing !== undefined) {
105
+ data.environment.current.memory.set(':=', existing);
106
+ }
107
+ if (head.value && outInfo.definitionRootNodes.length > 0) {
108
+ (0, built_in_assignment_1.markAsAssignment)(fnCall.information, { type: identifier_1.ReferenceType.Variable, name: head.value.lexeme ?? '', nodeId: head.value.info.id, definedAt: rootId, controlDependencies: [] }, outInfo.definitionRootNodes, rootId);
109
+ }
110
+ return fnCall;
111
+ }
112
+ /**
113
+ * Processes different types of string-based access operations.
114
+ *
115
+ * Example:
116
+ * ```r
117
+ * a$foo
118
+ * a@foo
119
+ * ```
120
+ */
121
+ function processStringBasedAccess(args, data, name, rootId, config) {
122
+ const newArgs = [...args];
123
+ // if the argument is a symbol, we convert it to a string for this perspective
124
+ for (let i = 1; i < newArgs.length; i++) {
125
+ const arg = newArgs[i];
126
+ if (arg !== r_function_call_1.EmptyArgument && arg.value?.type === type_1.RType.Symbol) {
127
+ newArgs[i] = {
128
+ ...arg,
129
+ value: {
130
+ type: type_1.RType.String,
131
+ info: arg.value.info,
132
+ lexeme: arg.value.lexeme,
133
+ location: arg.value.location,
134
+ content: {
135
+ quotes: 'none',
136
+ str: arg.value.lexeme
137
+ }
138
+ }
139
+ };
140
+ }
141
+ }
142
+ const fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args: newArgs, rootId, data, forceArgs: config.forceArgs });
143
+ // Resolve access on the way up the fold
144
+ const nonEmptyArgs = newArgs.filter(arg => arg !== r_function_call_1.EmptyArgument);
145
+ const accessedArg = nonEmptyArgs.find(arg => arg.info.role === "accessed" /* RoleInParent.Accessed */);
146
+ const accessArg = nonEmptyArgs.find(arg => arg.info.role === "index-access" /* RoleInParent.IndexAccess */);
147
+ if (accessedArg === undefined || accessArg === undefined) {
148
+ return fnCall;
149
+ }
150
+ let accessedIndicesCollection;
151
+ // If the accessedArg is a symbol, it's either a simple access or the base case of a nested access
152
+ if (accessedArg.value?.type === type_1.RType.Symbol) {
153
+ accessedIndicesCollection = (0, list_access_1.resolveSingleIndex)(accessedArg, accessArg, data.environment);
154
+ }
155
+ else {
156
+ // Higher access call
157
+ const underlyingAccessId = accessedArg.value?.info.id ?? -1;
158
+ const vertex = fnCall.information.graph.getVertex(underlyingAccessId);
159
+ const subIndices = vertex?.indicesCollection
160
+ ?.flatMap(indices => indices.indices)
161
+ ?.flatMap(index => index?.subIndices ?? []);
162
+ if (subIndices) {
163
+ accessedIndicesCollection = (0, list_access_1.filterIndices)(subIndices, accessArg);
164
+ }
165
+ }
166
+ // Add indices to vertex afterward
167
+ if (accessedIndicesCollection) {
168
+ const vertex = fnCall.information.graph.getVertex(rootId);
169
+ if (vertex) {
170
+ vertex.indicesCollection = accessedIndicesCollection;
171
+ }
172
+ // When access has no access as parent, it's the top most
173
+ const rootNode = data.completeAst.idMap.get(rootId);
174
+ const parentNode = data.completeAst.idMap.get(rootNode?.info.parent ?? -1);
175
+ if (parentNode?.type !== type_1.RType.Access) {
176
+ // Only reference indices in top most access
177
+ referenceIndices(accessedIndicesCollection, fnCall, name.info.id);
178
+ }
179
+ }
180
+ return fnCall;
181
+ }
182
+ /**
183
+ * Creates edges of type {@link EdgeType.Reads} to the accessed Indices and their sub-indices starting from
184
+ * the node with {@link parentNodeId}.
185
+ *
186
+ * @param accessedIndicesCollection - All indices that were accessed by the access operation
187
+ * @param fnCall - The {@link ProcessKnownFunctionCallResult} of the access operation
188
+ * @param parentNodeId - {@link NodeId} of the parent from which the edge starts
189
+ */
190
+ function referenceIndices(accessedIndicesCollection, fnCall, parentNodeId) {
191
+ const accessedIndices = accessedIndicesCollection?.flatMap(indices => indices.indices);
192
+ for (const accessedIndex of accessedIndices ?? []) {
193
+ fnCall.information.graph.addEdge(parentNodeId, accessedIndex.nodeId, edge_1.EdgeType.Reads);
194
+ const accessedSubIndices = (0, vertex_1.isParentContainerIndex)(accessedIndex) ? accessedIndex.subIndices : undefined;
195
+ referenceIndices(accessedSubIndices, fnCall, accessedIndex.nodeId);
196
+ }
197
+ }
106
198
  //# sourceMappingURL=built-in-access.js.map
@@ -4,17 +4,22 @@ import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/
4
4
  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
- import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
8
- import type { IdentifierDefinition } from '../../../../../environments/identifier';
7
+ import { type NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
8
+ import type { InGraphIdentifierDefinition } from '../../../../../environments/identifier';
9
+ import type { ContainerIndicesCollection } from '../../../../../graph/vertex';
9
10
  import type { ForceArguments } from '../common';
10
11
  import type { REnvironmentInformation } from '../../../../../environments/environment';
11
12
  import type { DataflowGraph } from '../../../../../graph/graph';
12
13
  export interface AssignmentConfiguration extends ForceArguments {
13
14
  readonly superAssignment?: boolean;
14
15
  readonly swapSourceAndTarget?: boolean;
16
+ /** Make maybe if assigned to symbol */
15
17
  readonly makeMaybe?: boolean;
16
18
  readonly quoteSource?: boolean;
17
19
  readonly canBeReplacement?: boolean;
20
+ /** is the target a variable pointing at the actual name? */
21
+ readonly targetVariable?: boolean;
22
+ readonly indicesCollection?: ContainerIndicesCollection;
18
23
  }
19
24
  /**
20
25
  * Processes an assignment, i.e., `<target> <- <source>`.
@@ -43,4 +48,4 @@ export interface AssignmentToSymbolParameters<OtherInfo> extends AssignmentConfi
43
48
  export declare function markAsAssignment(information: {
44
49
  environment: REnvironmentInformation;
45
50
  graph: DataflowGraph;
46
- }, nodeToDefine: IdentifierDefinition, sourceIds: readonly NodeId[], rootIdOfAssignment: NodeId, quoteSource?: boolean, superAssignment?: boolean): void;
51
+ }, nodeToDefine: InGraphIdentifierDefinition, sourceIds: readonly NodeId[], rootIdOfAssignment: NodeId, config?: AssignmentConfiguration | undefined): void;
@@ -16,6 +16,8 @@ const retriever_1 = require("../../../../../../r-bridge/retriever");
16
16
  const vertex_1 = require("../../../../../graph/vertex");
17
17
  const define_1 = require("../../../../../environments/define");
18
18
  const edge_1 = require("../../../../../graph/edge");
19
+ const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
20
+ const list_access_1 = require("../../../../../../util/list-access");
19
21
  function toReplacementSymbol(target, prefix, superAssignment) {
20
22
  return {
21
23
  type: type_1.RType.Symbol,
@@ -57,7 +59,7 @@ args, rootId, data, config) {
57
59
  const effectiveArgs = getEffectiveOrder(config, args);
58
60
  const { target, source } = extractSourceAndTarget(effectiveArgs, name);
59
61
  const { type, named } = target;
60
- if (type === type_1.RType.Symbol) {
62
+ if (!config.targetVariable && type === type_1.RType.Symbol) {
61
63
  const res = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, reverseOrder: !config.swapSourceAndTarget, forceArgs: config.forceArgs });
62
64
  return processAssignmentToSymbol({
63
65
  ...config,
@@ -107,8 +109,10 @@ args, rootId, data, config) {
107
109
  else if (type === type_1.RType.String) {
108
110
  return processAssignmentToString(target, args, name, rootId, data, config, source);
109
111
  }
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;
112
+ logger_1.dataflowLogger.warn(`Assignment ${name.content} has an unknown target type ${target.type} => unknown impact`);
113
+ const info = (0, known_call_handling_1.processKnownFunctionCall)({ name, args: effectiveArgs, rootId, data, forceArgs: config.forceArgs }).information;
114
+ info.graph.markIdForUnknownSideEffects(rootId);
115
+ return info;
112
116
  }
113
117
  function extractSourceAndTarget(args, name) {
114
118
  const source = (0, unpack_argument_1.unpackArgument)(args[1], false);
@@ -117,12 +121,16 @@ function extractSourceAndTarget(args, name) {
117
121
  (0, assert_1.guard)(target !== undefined, () => `Assignment ${name.content} has no target, impossible!`);
118
122
  return { source, target };
119
123
  }
120
- function produceWrittenNodes(rootId, target, referenceType, data, makeMaybe) {
124
+ /**
125
+ * Promotes the ingoing/unknown references of target (an assignment) to definitions
126
+ */
127
+ function produceWrittenNodes(rootId, target, referenceType, data, makeMaybe, value) {
121
128
  return [...target.in, ...target.unknownReferences].map(ref => ({
122
129
  ...ref,
123
130
  type: referenceType,
124
131
  definedAt: rootId,
125
- controlDependencies: data.controlDependencies ?? (makeMaybe ? [] : undefined)
132
+ controlDependencies: data.controlDependencies ?? (makeMaybe ? [] : undefined),
133
+ value: value
126
134
  }));
127
135
  }
128
136
  function processAssignmentToString(target, args, name, rootId, data, config, source) {
@@ -179,10 +187,29 @@ function checkTargetReferenceType(source, sourceInfo) {
179
187
  * @param quoteSource - whether to quote the source (i.e., define `x` without a direct reference to `v`)
180
188
  * @param superAssignment - whether this is a super assignment (i.e., `<<-`)
181
189
  */
182
- function markAsAssignment(information, nodeToDefine, sourceIds, rootIdOfAssignment, quoteSource, superAssignment) {
183
- information.environment = (0, define_1.define)(nodeToDefine, superAssignment, information.environment);
190
+ function markAsAssignment(information, nodeToDefine, sourceIds, rootIdOfAssignment, config) {
191
+ let indicesCollection = undefined;
192
+ if (sourceIds.length === 1) {
193
+ // support for tracking indices
194
+ // Indices were defined for the vertex e.g. a <- list(c = 1) or a$b <- list(c = 1)
195
+ indicesCollection = information.graph.getVertex(sourceIds[0])?.indicesCollection;
196
+ }
197
+ // Indices defined by replacement operation e.g. $<-
198
+ if (config?.indicesCollection !== undefined) {
199
+ // If there were indices stored in the vertex, then a container was defined
200
+ // and assigned to the index of another container e.g. a$b <- list(c = 1)
201
+ if (indicesCollection) {
202
+ indicesCollection = (0, list_access_1.addSubIndicesToLeafIndices)(config.indicesCollection, indicesCollection);
203
+ }
204
+ else {
205
+ // No indices were defined for the vertex e.g. a$b <- 2
206
+ indicesCollection = config.indicesCollection;
207
+ }
208
+ }
209
+ nodeToDefine.indicesCollection ??= indicesCollection;
210
+ information.environment = (0, define_1.define)(nodeToDefine, config?.superAssignment, information.environment);
184
211
  information.graph.setDefinitionOfVertex(nodeToDefine);
185
- if (!quoteSource) {
212
+ if (!config?.quoteSource) {
186
213
  for (const sourceId of sourceIds) {
187
214
  information.graph.addEdge(nodeToDefine, sourceId, edge_1.EdgeType.DefinedBy);
188
215
  }
@@ -200,9 +227,11 @@ function markAsAssignment(information, nodeToDefine, sourceIds, rootIdOfAssignme
200
227
  /**
201
228
  * Helper function whenever it is known that the _target_ of an assignment is a (single) symbol (i.e. `x <- ...`, but not `names(x) <- ...`).
202
229
  */
203
- function processAssignmentToSymbol({ nameOfAssignmentFunction, source, args: [targetArg, sourceArg], target, rootId, data, information, superAssignment, makeMaybe, quoteSource }) {
230
+ function processAssignmentToSymbol(config) {
231
+ const { nameOfAssignmentFunction, source, args: [targetArg, sourceArg], target, rootId, data, information, makeMaybe, quoteSource } = config;
204
232
  const referenceType = checkTargetReferenceType(source, sourceArg);
205
- const writeNodes = produceWrittenNodes(rootId, targetArg, referenceType, data, makeMaybe ?? false);
233
+ const aliases = (0, resolve_by_name_1.getAliases)([source.info.id], information.graph, information.environment);
234
+ const writeNodes = produceWrittenNodes(rootId, targetArg, referenceType, data, makeMaybe ?? false, aliases);
206
235
  if (writeNodes.length !== 1 && log_1.log.settings.minLevel <= 4 /* LogLevel.Warn */) {
207
236
  log_1.log.warn(`Unexpected write number in assignment: ${JSON.stringify(writeNodes)}`);
208
237
  }
@@ -215,7 +244,7 @@ function processAssignmentToSymbol({ nameOfAssignmentFunction, source, args: [ta
215
244
  information.environment = (0, overwrite_1.overwriteEnvironment)(targetArg.environment, sourceArg.environment);
216
245
  // install assigned variables in environment
217
246
  for (const write of writeNodes) {
218
- markAsAssignment(information, write, [source.info.id], rootId, quoteSource, superAssignment);
247
+ markAsAssignment(information, write, [source.info.id], rootId, config);
219
248
  }
220
249
  information.graph.addEdge(rootId, targetArg.entryPoint, edge_1.EdgeType.Returns);
221
250
  if (quoteSource) {
@@ -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;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.processFunctionDefinition = processFunctionDefinition;
4
+ exports.retrieveActiveEnvironment = retrieveActiveEnvironment;
5
+ exports.updateNestedFunctionClosures = updateNestedFunctionClosures;
6
+ exports.updateNestedFunctionCalls = updateNestedFunctionCalls;
4
7
  const processor_1 = require("../../../../../processor");
5
8
  const linker_1 = require("../../../../linker");
6
9
  const known_call_handling_1 = require("../known-call-handling");
@@ -77,7 +80,7 @@ function processFunctionDefinition(name, args, rootId, data) {
77
80
  graph: new Set(subgraph.rootIds()),
78
81
  environment: outEnvironment
79
82
  };
80
- updateNestedFunctionClosures(subgraph, outEnvironment, name);
83
+ updateNestedFunctionClosures(subgraph, outEnvironment, name.info.id);
81
84
  const exitPoints = body.exitPoints;
82
85
  const graph = new graph_1.DataflowGraph(data.completeAst.idMap).mergeWith(subgraph, false);
83
86
  graph.addVertex({
@@ -99,10 +102,32 @@ function processFunctionDefinition(name, args, rootId, data) {
99
102
  environment: originalEnvironment
100
103
  };
101
104
  }
102
- function updateNestedFunctionClosures(subgraph, outEnvironment, name) {
105
+ // this is no longer necessary when we update environments to be back to front (e.g., with a list of environments)
106
+ // this favors the bigger environment
107
+ function retrieveActiveEnvironment(callerEnvironment, baseEnvironment) {
108
+ callerEnvironment ??= (0, environment_1.initializeCleanEnvironments)(true);
109
+ let level = callerEnvironment.level ?? 0;
110
+ if (baseEnvironment.level !== level) {
111
+ while (baseEnvironment.level < level) {
112
+ baseEnvironment = (0, scoping_1.pushLocalEnvironment)(baseEnvironment);
113
+ }
114
+ while (baseEnvironment.level > level) {
115
+ callerEnvironment = (0, scoping_1.pushLocalEnvironment)(callerEnvironment);
116
+ level = callerEnvironment.level;
117
+ }
118
+ }
119
+ return (0, overwrite_1.overwriteEnvironment)(baseEnvironment, callerEnvironment);
120
+ }
121
+ /**
122
+ * Update the closure links of all nested function definitions
123
+ * @param graph - dataflow graph to collect the function definitions from and to update the closure links for
124
+ * @param outEnvironment - active environment on resolving closures (i.e., exit of the function definition)
125
+ * @param fnId - id of the function definition to update the closure links for
126
+ */
127
+ function updateNestedFunctionClosures(graph, outEnvironment, fnId) {
103
128
  // track *all* function definitions - including those nested within the current graph,
104
129
  // try to resolve their 'in' by only using the lowest scope which will be popped after this definition
105
- for (const [id, { subflow, tag }] of subgraph.vertices(true)) {
130
+ for (const [id, { subflow, tag }] of graph.vertices(true)) {
106
131
  if (tag !== vertex_1.VertexType.FunctionDefinition) {
107
132
  continue;
108
133
  }
@@ -114,15 +139,67 @@ function updateNestedFunctionClosures(subgraph, outEnvironment, name) {
114
139
  remainingIn.push(ingoing);
115
140
  continue;
116
141
  }
117
- (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${id} in closure of function definition ${name.info.id}`);
142
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${id} in closure of function definition ${fnId}`);
118
143
  for (const ref of resolved) {
119
- subgraph.addEdge(ingoing, ref, edge_1.EdgeType.Reads);
144
+ graph.addEdge(ingoing, ref, edge_1.EdgeType.Reads);
120
145
  }
121
146
  }
122
- (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Keeping ${remainingIn.length} references to open ref ${id} in closure of function definition ${name.info.id}`);
147
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Keeping ${remainingIn.length} references to open ref ${id} in closure of function definition ${fnId}`);
123
148
  subflow.in = remainingIn;
124
149
  }
125
150
  }
151
+ /**
152
+ * Update the closure links of all nested function calls, this is probably to be done once at the end of the script
153
+ * @param graph - dataflow graph to collect the function calls from and to update the closure links for
154
+ * @param outEnvironment - active environment on resolving closures (i.e., exit of the function definition)
155
+ */
156
+ function updateNestedFunctionCalls(graph, outEnvironment) {
157
+ // track *all* function definitions - including those nested within the current graph,
158
+ // try to resolve their 'in' by only using the lowest scope which will be popped after this definition
159
+ for (const [id, { onlyBuiltin, tag, environment, name }] of graph.vertices(true)) {
160
+ if (tag !== vertex_1.VertexType.FunctionCall || !name || onlyBuiltin) {
161
+ continue;
162
+ }
163
+ // only the call environment counts!
164
+ if (environment) {
165
+ while (outEnvironment.level > environment.level) {
166
+ outEnvironment = (0, scoping_1.popLocalEnvironment)(outEnvironment);
167
+ }
168
+ while (outEnvironment.level < environment.level) {
169
+ outEnvironment = (0, scoping_1.pushLocalEnvironment)(outEnvironment);
170
+ }
171
+ }
172
+ const effectiveEnvironment = environment ? (0, overwrite_1.overwriteEnvironment)(outEnvironment, environment) : outEnvironment;
173
+ const targets = (0, linker_1.getAllFunctionCallTargets)(id, graph, effectiveEnvironment);
174
+ for (const target of targets) {
175
+ const targetVertex = graph.getVertex(target);
176
+ if (targetVertex?.tag !== vertex_1.VertexType.FunctionDefinition) {
177
+ // support reads on symbols
178
+ if (targetVertex?.tag === vertex_1.VertexType.Use) {
179
+ graph.addEdge(id, target, edge_1.EdgeType.Reads);
180
+ }
181
+ continue;
182
+ }
183
+ graph.addEdge(id, target, edge_1.EdgeType.Calls);
184
+ const ingoingRefs = targetVertex.subflow.in;
185
+ const remainingIn = [];
186
+ for (const ingoing of ingoingRefs) {
187
+ const resolved = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, effectiveEnvironment, ingoing.type) : undefined;
188
+ if (resolved === undefined) {
189
+ remainingIn.push(ingoing);
190
+ continue;
191
+ }
192
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${id} in closure of function definition ${id}`);
193
+ for (const def of resolved) {
194
+ graph.addEdge(ingoing, def, edge_1.EdgeType.DefinedByOnCall);
195
+ graph.addEdge(id, def, edge_1.EdgeType.DefinesOnCall);
196
+ }
197
+ }
198
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Keeping ${remainingIn.length} references to open ref ${id} in closure of function definition ${id}`);
199
+ targetVertex.subflow.in = remainingIn;
200
+ }
201
+ }
202
+ }
126
203
  function prepareFunctionEnvironment(data) {
127
204
  let env = (0, environment_1.initializeCleanEnvironments)();
128
205
  for (let i = 0; i < data.environment.level + 1 /* add another env */; i++) {
@@ -33,9 +33,9 @@ function processIfThenElse(name, args, rootId, data) {
33
33
  let then;
34
34
  let makeThenMaybe = false;
35
35
  // we should defer this to the abstract interpretation
36
- const definitions = (0, resolve_by_name_1.resolveToConstants)(condArg?.lexeme, data.environment);
37
- const conditionIsAlwaysFalse = definitions?.every(d => d.value === false) ?? false;
38
- const conditionIsAlwaysTrue = definitions?.every(d => d.value === true) ?? false;
36
+ const values = (0, resolve_by_name_1.resolveValueOfVariable)(condArg?.lexeme, data.environment, cond.graph);
37
+ const conditionIsAlwaysFalse = values?.every(d => d === false) ?? false;
38
+ const conditionIsAlwaysTrue = values?.every(d => d === true) ?? false;
39
39
  if (!conditionIsAlwaysFalse) {
40
40
  then = (0, processor_1.processDataflowFor)(thenArg, data);
41
41
  if (then.entryPoint) {
@@ -0,0 +1,15 @@
1
+ import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
2
+ import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
3
+ import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
4
+ import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
5
+ import type { DataflowInformation } from '../../../../../info';
6
+ import type { DataflowProcessorInformation } from '../../../../../processor';
7
+ /**
8
+ * Process a list call.
9
+ *
10
+ * Example:
11
+ * ```r
12
+ * list(a = 1, b = 2)
13
+ * ```
14
+ */
15
+ export declare function processList<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processList = processList;
4
+ const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
5
+ const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
6
+ const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
7
+ const known_call_handling_1 = require("../known-call-handling");
8
+ const config_1 = require("../../../../../../config");
9
+ /**
10
+ * Process a list call.
11
+ *
12
+ * Example:
13
+ * ```r
14
+ * list(a = 1, b = 2)
15
+ * ```
16
+ */
17
+ function processList(name, args, rootId, data) {
18
+ if (!(0, config_1.getConfig)().solver.pointerTracking) {
19
+ return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
20
+ }
21
+ const namedArguments = [];
22
+ for (const arg of args) {
23
+ // Skip non named arguments
24
+ if (arg === r_function_call_1.EmptyArgument || arg.type !== type_1.RType.Argument || arg.name === undefined) {
25
+ continue;
26
+ }
27
+ let newIndex = {
28
+ lexeme: arg.name.content,
29
+ nodeId: arg.info.id,
30
+ };
31
+ // Check whether argument value is non-primitive
32
+ if (arg.value?.type === type_1.RType.Symbol) {
33
+ const defs = (0, resolve_by_name_1.resolveByName)(arg.value.lexeme, data.environment);
34
+ const indices = defs?.flatMap(index => index.indicesCollection ?? []);
35
+ if (indices) {
36
+ newIndex = {
37
+ ...newIndex,
38
+ subIndices: indices,
39
+ };
40
+ }
41
+ }
42
+ namedArguments.push(newIndex);
43
+ }
44
+ const indices = {
45
+ indices: namedArguments,
46
+ isContainer: true,
47
+ };
48
+ return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }, [indices]).information;
49
+ }
50
+ //# sourceMappingURL=built-in-list.js.map
@@ -3,7 +3,7 @@ import type { DataflowInformation } from '../../../../../info';
3
3
  import type { ForceArguments } from '../common';
4
4
  import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
5
5
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
- import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
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
8
  export declare function processReplacementFunction<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>,
9
9
  /** The last one has to be the value */
@@ -7,11 +7,14 @@ const log_1 = require("../../../../../../util/log");
7
7
  const built_in_assignment_1 = require("./built-in-assignment");
8
8
  const common_1 = require("../common");
9
9
  const assert_1 = require("../../../../../../util/assert");
10
+ const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
10
11
  const logger_1 = require("../../../../../logger");
11
12
  const vertex_1 = require("../../../../../graph/vertex");
12
13
  const graph_1 = require("../../../../../graph/graph");
13
14
  const edge_1 = require("../../../../../graph/edge");
14
15
  const dfg_1 = require("../../../../../../util/mermaid/dfg");
16
+ const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
17
+ const list_access_1 = require("../../../../../../util/list-access");
15
18
  function processReplacementFunction(name,
16
19
  /** The last one has to be the value */
17
20
  args, rootId, data, config) {
@@ -21,8 +24,33 @@ args, rootId, data, config) {
21
24
  }
22
25
  /* we only get here if <-, <<-, ... or whatever is part of the replacement is not overwritten */
23
26
  (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Replacement ${name.content} with ${JSON.stringify(args)}, processing`);
27
+ let indices = undefined;
28
+ if (name.content === '$<-') {
29
+ const nonEmptyArgs = args.filter(arg => arg !== r_function_call_1.EmptyArgument);
30
+ const accessedArg = nonEmptyArgs.find(arg => arg.info.role === "accessed" /* RoleInParent.Accessed */);
31
+ const accessArg = nonEmptyArgs.find(arg => arg.info.role === "index-access" /* RoleInParent.IndexAccess */);
32
+ if (accessArg !== undefined && accessedArg != undefined) {
33
+ const leafIndex = { lexeme: accessArg.lexeme, nodeId: accessedArg.info.parent ?? '' };
34
+ const accessIndices = {
35
+ indices: [leafIndex],
36
+ isContainer: false
37
+ };
38
+ // Check for nested access
39
+ if (accessedArg.value?.type === type_1.RType.Access) {
40
+ indices = (0, list_access_1.constructNestedAccess)(accessedArg.value, accessIndices);
41
+ }
42
+ else {
43
+ // use access node as reference to get complete line in slice
44
+ indices = [accessIndices];
45
+ }
46
+ }
47
+ }
24
48
  /* we assign the first argument by the last for now and maybe mark as maybe!, we can keep the symbol as we now know we have an assignment */
25
- const res = (0, built_in_assignment_1.processAssignment)(name, [args[0], args[args.length - 1]], rootId, data, { superAssignment: config.assignmentOperator === '<<-', makeMaybe: config.makeMaybe });
49
+ const res = (0, built_in_assignment_1.processAssignment)(name, [args[0], args[args.length - 1]], rootId, data, {
50
+ superAssignment: config.assignmentOperator === '<<-',
51
+ makeMaybe: indices !== undefined ? false : config.makeMaybe,
52
+ indicesCollection: indices
53
+ });
26
54
  /* now, we soft-inject other arguments, so that calls like `x[y] <- 3` are linked correctly */
27
55
  const { callArgs } = (0, common_1.processAllArguments)({
28
56
  functionName: (0, info_1.initializeCleanDataflowInformation)(rootId, data),