@eagleoutice/flowr 2.0.20 → 2.0.22

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.
@@ -109,9 +109,10 @@ registerBuiltInConstant(true, 'T', true);
109
109
  registerBuiltInConstant(true, 'FALSE', false);
110
110
  registerBuiltInConstant(true, 'F', false);
111
111
  registerSimpleFunctions('~', '+', '-', '*', '/', '^', '!', '?', '**', '==', '!=', '>', '<', '>=', '<=', '%%', '%/%', '%*%', '%in%', ':', 'list', 'c', 'rep', 'seq', 'seq_len', 'seq_along', 'seq.int', 'gsub', 'which', 'class', 'dimnames', 'min', 'max', 'intersect', 'subset', 'match', 'sqrt', 'abs', 'round', 'floor', 'ceiling', 'signif', 'trunc', 'log', 'log10', 'log2', 'sum', 'mean', 'unique', 'paste', 'paste0', 'read.csv', 'stop', 'is.null', 'plot', 'numeric', 'as.character', 'as.integer', 'as.logical', 'as.numeric', 'as.matrix', 'do.call', 'rbind', 'nrow', 'ncol', 'tryCatch', 'expression', 'factor', 'missing', 'as.data.frame', 'data.frame', 'na.omit', 'rownames', 'names', 'order', 'length', 'any', 'dim', 'matrix', 'cbind', 'nchar', 't');
112
- registerBuiltInFunctions(false, ['lapply', 'sapply', 'vapply', 'mapply'], built_in_apply_1.processApply, { indexOfFunction: 1, nameOfFunctionArgument: 'FUN' });
112
+ registerBuiltInFunctions(false, ['mapply', 'Mapply'], built_in_apply_1.processApply, { indexOfFunction: 0, nameOfFunctionArgument: 'FUN' });
113
+ registerBuiltInFunctions(false, ['lapply', 'sapply', 'vapply'], built_in_apply_1.processApply, { indexOfFunction: 1, nameOfFunctionArgument: 'FUN' });
113
114
  /* functool wrappers */
114
- registerBuiltInFunctions(false, ['Lapply', 'Sapply', 'Vapply', 'Mapply'], built_in_apply_1.processApply, { indexOfFunction: 1, nameOfFunctionArgument: 'FUN' });
115
+ registerBuiltInFunctions(false, ['Lapply', 'Sapply', 'Vapply'], built_in_apply_1.processApply, { indexOfFunction: 1, nameOfFunctionArgument: 'FUN' });
115
116
  registerBuiltInFunctions(false, ['apply', 'tapply', 'Tapply'], built_in_apply_1.processApply, { indexOfFunction: 2, nameOfFunctionArgument: 'FUN' });
116
117
  registerBuiltInFunctions(false, ['print'], defaultBuiltInProcessor, { returnsNthArgument: 0, forceArgs: 'all' });
117
118
  registerBuiltInFunctions(true, ['('], defaultBuiltInProcessor, { returnsNthArgument: 0 });
@@ -143,7 +144,7 @@ registerBuiltInFunctions(true, ['for'], built_in_for_loop_1.processForLoop, {});
143
144
  registerBuiltInFunctions(true, ['repeat'], built_in_repeat_loop_1.processRepeatLoop, {});
144
145
  registerBuiltInFunctions(true, ['while'], built_in_while_loop_1.processWhileLoop, {});
145
146
  registerBuiltInFunctions(false, ['options'], defaultBuiltInProcessor, { hasUnknownSideEffects: true, forceArgs: 'all' });
146
- registerBuiltInFunctions(false, ['on.exit', 'sys.on.exit'], defaultBuiltInProcessor, { hasUnknownSideEffects: true });
147
+ registerBuiltInFunctions(false, ['on.exit', 'sys.on.exit', 'par'], defaultBuiltInProcessor, { hasUnknownSideEffects: true });
147
148
  /* library and require is handled above */
148
149
  registerBuiltInFunctions(false, ['requireNamespace', 'loadNamespace', 'attachNamespace', 'asNamespace'], defaultBuiltInProcessor, { hasUnknownSideEffects: true });
149
150
  /* downloader and installer functions (R, devtools, BiocManager) */
@@ -25,6 +25,18 @@ function toReplacementSymbol(target, prefix, superAssignment) {
25
25
  function getEffectiveOrder(config, args) {
26
26
  return config.swapSourceAndTarget ? [args[1], args[0]] : args;
27
27
  }
28
+ function findRootAccess(node) {
29
+ let current = node;
30
+ while (current.type === "RAccess" /* RType.Access */) {
31
+ current = current.accessed;
32
+ }
33
+ if (current.type === "RSymbol" /* RType.Symbol */) {
34
+ return current;
35
+ }
36
+ else {
37
+ return undefined;
38
+ }
39
+ }
28
40
  /**
29
41
  * Processes an assignment, i.e., `<target> <- <source>`.
30
42
  * Handling it as a function call \`&lt;-\` `(<target>, <source>)`.
@@ -64,6 +76,29 @@ args, rootId, data, config) {
64
76
  const replacement = toReplacementSymbol(target, target.operator, config.superAssignment ?? false);
65
77
  return (0, process_named_call_1.processAsNamedCall)(replacement, data, replacement.content, [(0, make_argument_1.toUnnamedArgument)(target.accessed, data.completeAst.idMap), ...target.access, source]);
66
78
  }
79
+ else if (type === "RAccess" /* RType.Access */) {
80
+ const rootArg = findRootAccess(target);
81
+ if (rootArg) {
82
+ const res = (0, known_call_handling_1.processKnownFunctionCall)({
83
+ name,
84
+ args: [rootArg, source],
85
+ rootId,
86
+ data,
87
+ reverseOrder: !config.swapSourceAndTarget,
88
+ forceArgs: config.forceArgs
89
+ });
90
+ return processAssignmentToSymbol({
91
+ ...config,
92
+ nameOfAssignmentFunction: name.content,
93
+ source,
94
+ target: rootArg,
95
+ args: getEffectiveOrder(config, res.processedArguments),
96
+ rootId,
97
+ data,
98
+ information: res.information,
99
+ });
100
+ }
101
+ }
67
102
  else if (type === "RString" /* RType.String */) {
68
103
  return processAssignmentToString(target, args, name, rootId, data, config, source);
69
104
  }
@@ -79,7 +114,7 @@ function extractSourceAndTarget(args, name) {
79
114
  return { source, target };
80
115
  }
81
116
  function produceWrittenNodes(rootId, target, isFunctionDef, data, makeMaybe) {
82
- return target.in.map(ref => ({
117
+ return [...target.in, ...target.unknownReferences].map(ref => ({
83
118
  ...ref,
84
119
  kind: isFunctionDef ? 'function' : 'variable',
85
120
  definedAt: rootId,
@@ -63,7 +63,7 @@ function processForLoop(name, args, rootId, data) {
63
63
  return {
64
64
  unknownReferences: [],
65
65
  // we only want those not bound by a local variable
66
- in: [{ nodeId: rootId, name: name.content, controlDependencies: originalDependency }, ...variable.in, ...[...nameIdShares.values()].flat()],
66
+ in: [{ nodeId: rootId, name: name.content, controlDependencies: originalDependency }, ...variable.in, ...vector.in, ...vector.unknownReferences, ...[...nameIdShares.values()].flat()],
67
67
  out: outgoing,
68
68
  graph: nextGraph,
69
69
  entryPoint: name.info.id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.0.20",
3
+ "version": "2.0.22",
4
4
  "description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "repository": {
@@ -44,11 +44,11 @@ export declare function deterministicLocationIdGenerator<OtherInfo>(start?: numb
44
44
  export interface ParentContextInfo extends MergeableRecord {
45
45
  role: RoleInParent;
46
46
  /**
47
- * The depth of the node in the AST
47
+ * The nesting of the node in the AST
48
48
  *
49
- * The root node has a depth of 0, its children a depth of 1, and so on.
49
+ * The root node has a nesting of 0, nested function calls, loops etc. will increase the nesting
50
50
  */
51
- depth: number;
51
+ nesting: number;
52
52
  /**
53
53
  * 0-based index of the child in the parent (code semantics, e.g., for an if-then-else, the condition will be 0, the then-case will be 1, ...)
54
54
  *
@@ -60,6 +60,9 @@ const defaultParentContext = {
60
60
  role: "root" /* RoleInParent.Root */,
61
61
  index: 0
62
62
  };
63
+ const nestForElement = new Set([
64
+ "RFunctionDefinition" /* RType.FunctionDefinition */, "RForLoop" /* RType.ForLoop */, "RWhileLoop" /* RType.WhileLoop */, "RRepeatLoop" /* RType.RepeatLoop */, "RIfThenElse" /* RType.IfThenElse */,
65
+ ]);
63
66
  /**
64
67
  * Covert the given AST into a doubly linked tree while assigning ids (so it stays serializable).
65
68
  *
@@ -77,9 +80,16 @@ function decorateAst(ast, getId = deterministicCountingIdGenerator(0)) {
77
80
  const foldLeaf = createFoldForLeaf(info);
78
81
  const foldBinaryOp = createFoldForBinaryOp(info);
79
82
  const unaryOp = createFoldForUnaryOp(info);
80
- /* we pass down the depth */
81
- const decoratedAst = (0, stateful_fold_1.foldAstStateful)(ast, -1, {
82
- down: (_, down) => down + 1,
83
+ /* we pass down the nesting depth */
84
+ const decoratedAst = (0, stateful_fold_1.foldAstStateful)(ast, 0, {
85
+ down: (n, nesting) => {
86
+ if (nestForElement.has(n.type)) {
87
+ return nesting + 1;
88
+ }
89
+ else {
90
+ return nesting;
91
+ }
92
+ },
83
93
  foldNumber: foldLeaf,
84
94
  foldString: foldLeaf,
85
95
  foldLogical: foldLeaf,
@@ -117,7 +127,7 @@ function decorateAst(ast, getId = deterministicCountingIdGenerator(0)) {
117
127
  }
118
128
  exports.decorateAst = decorateAst;
119
129
  function createFoldForLeaf(info) {
120
- return (data, depth) => {
130
+ return (data, nesting) => {
121
131
  const id = info.getId(data);
122
132
  const decorated = {
123
133
  ...data,
@@ -126,7 +136,7 @@ function createFoldForLeaf(info) {
126
136
  id,
127
137
  parent: undefined,
128
138
  ...defaultParentContext,
129
- depth
139
+ nesting
130
140
  }
131
141
  };
132
142
  info.idMap.set(id, decorated);
@@ -134,9 +144,9 @@ function createFoldForLeaf(info) {
134
144
  };
135
145
  }
136
146
  function createFoldForBinaryOp(info) {
137
- return (data, lhs, rhs, depth) => {
147
+ return (data, lhs, rhs, nesting) => {
138
148
  const id = info.getId(data);
139
- const decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, lhs, rhs };
149
+ const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, lhs, rhs };
140
150
  info.idMap.set(id, decorated);
141
151
  const lhsInfo = lhs.info;
142
152
  lhsInfo.parent = id;
@@ -155,9 +165,9 @@ function createFoldForBinaryOp(info) {
155
165
  };
156
166
  }
157
167
  function createFoldForUnaryOp(info) {
158
- return (data, operand, depth) => {
168
+ return (data, operand, nesting) => {
159
169
  const id = info.getId(data);
160
- const decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, operand };
170
+ const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, operand };
161
171
  info.idMap.set(id, decorated);
162
172
  const opInfo = operand.info;
163
173
  opInfo.parent = id;
@@ -166,9 +176,9 @@ function createFoldForUnaryOp(info) {
166
176
  };
167
177
  }
168
178
  function createFoldForAccess(info) {
169
- return (data, accessed, access, depth) => {
179
+ return (data, accessed, access, nesting) => {
170
180
  const id = info.getId(data);
171
- const decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, accessed, access };
181
+ const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, accessed, access };
172
182
  info.idMap.set(id, decorated);
173
183
  const accessedInfo = accessed.info;
174
184
  accessedInfo.parent = id;
@@ -189,9 +199,9 @@ function createFoldForAccess(info) {
189
199
  };
190
200
  }
191
201
  function createFoldForForLoop(info) {
192
- return (data, variable, vector, body, depth) => {
202
+ return (data, variable, vector, body, nesting) => {
193
203
  const id = info.getId(data);
194
- const decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, variable, vector, body };
204
+ const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, variable, vector, body };
195
205
  info.idMap.set(id, decorated);
196
206
  const varInfo = variable.info;
197
207
  varInfo.parent = id;
@@ -208,9 +218,9 @@ function createFoldForForLoop(info) {
208
218
  };
209
219
  }
210
220
  function createFoldForRepeatLoop(info) {
211
- return (data, body, depth) => {
221
+ return (data, body, nesting) => {
212
222
  const id = info.getId(data);
213
- const decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, body };
223
+ const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, body };
214
224
  info.idMap.set(id, decorated);
215
225
  const bodyInfo = body.info;
216
226
  bodyInfo.parent = id;
@@ -219,9 +229,9 @@ function createFoldForRepeatLoop(info) {
219
229
  };
220
230
  }
221
231
  function createFoldForWhileLoop(info) {
222
- return (data, condition, body, depth) => {
232
+ return (data, condition, body, nesting) => {
223
233
  const id = info.getId(data);
224
- const decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, condition, body };
234
+ const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, condition, body };
225
235
  info.idMap.set(id, decorated);
226
236
  const condInfo = condition.info;
227
237
  condInfo.parent = id;
@@ -234,9 +244,9 @@ function createFoldForWhileLoop(info) {
234
244
  };
235
245
  }
236
246
  function createFoldForIfThenElse(info) {
237
- return (data, condition, then, otherwise, depth) => {
247
+ return (data, condition, then, otherwise, nesting) => {
238
248
  const id = info.getId(data);
239
- const decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, condition, then, otherwise };
249
+ const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, condition, then, otherwise };
240
250
  info.idMap.set(id, decorated);
241
251
  const condInfo = condition.info;
242
252
  condInfo.parent = id;
@@ -255,9 +265,9 @@ function createFoldForIfThenElse(info) {
255
265
  };
256
266
  }
257
267
  function createFoldForExprList(info) {
258
- return (data, grouping, children, depth) => {
268
+ return (data, grouping, children, nesting) => {
259
269
  const id = info.getId(data);
260
- const decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, grouping, children };
270
+ const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, grouping, children };
261
271
  info.idMap.set(id, decorated);
262
272
  let i = 0;
263
273
  for (const child of children) {
@@ -270,14 +280,14 @@ function createFoldForExprList(info) {
270
280
  };
271
281
  }
272
282
  function createFoldForFunctionCall(info) {
273
- return (data, functionName, args, depth) => {
283
+ return (data, functionName, args, nesting) => {
274
284
  const id = info.getId(data);
275
285
  let decorated;
276
286
  if (data.named) {
277
- decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, functionName, arguments: args };
287
+ decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, functionName, arguments: args };
278
288
  }
279
289
  else {
280
- decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, calledFunction: functionName, arguments: args };
290
+ decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, calledFunction: functionName, arguments: args };
281
291
  }
282
292
  info.idMap.set(id, decorated);
283
293
  const funcInfo = functionName.info;
@@ -297,9 +307,9 @@ function createFoldForFunctionCall(info) {
297
307
  };
298
308
  }
299
309
  function createFoldForFunctionDefinition(info) {
300
- return (data, params, body, depth) => {
310
+ return (data, params, body, nesting) => {
301
311
  const id = info.getId(data);
302
- const decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, parameters: params, body };
312
+ const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, parameters: params, body };
303
313
  info.idMap.set(id, decorated);
304
314
  let idx = 0;
305
315
  for (const param of params) {
@@ -316,9 +326,9 @@ function createFoldForFunctionDefinition(info) {
316
326
  };
317
327
  }
318
328
  function createFoldForFunctionParameter(info) {
319
- return (data, name, defaultValue, depth) => {
329
+ return (data, name, defaultValue, nesting) => {
320
330
  const id = info.getId(data);
321
- const decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, name, defaultValue };
331
+ const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, name, defaultValue };
322
332
  info.idMap.set(id, decorated);
323
333
  const nameInfo = name.info;
324
334
  nameInfo.parent = id;
@@ -333,9 +343,9 @@ function createFoldForFunctionParameter(info) {
333
343
  };
334
344
  }
335
345
  function createFoldForFunctionArgument(info) {
336
- return (data, name, value, depth) => {
346
+ return (data, name, value, nesting) => {
337
347
  const id = info.getId(data);
338
- const decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, name, value };
348
+ const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, name, value };
339
349
  info.idMap.set(id, decorated);
340
350
  let idx = 0;
341
351
  if (name) {
@@ -9,7 +9,7 @@ export declare const slicerLogger: import("tslog").Logger<import("tslog").ILogOb
9
9
  * The returned ids can be used to {@link reconstructToCode|reconstruct the slice to R code}.
10
10
  *
11
11
  * @param graph - The dataflow graph to conduct the slicing on.
12
- * @param ast - The normalized AST of the code (used to get static depth information of the lexemes in case of control flow dependencies that may have no effect on the slicing scope).
12
+ * @param ast - The normalized AST of the code (used to get static nesting information of the lexemes in case of control flow dependencies that may have no effect on the slicing scope).
13
13
  * @param criteria - The criteras to slice on.
14
14
  * @param threshold - The maximum number of nodes to visit in the graph. If the threshold is reached, the slice will side with inclusion and drop its minimal guarantee. The limit ensures that the algorithm halts.
15
15
  */
@@ -16,7 +16,7 @@ exports.slicerLogger = log_1.log.getSubLogger({ name: 'slicer' });
16
16
  * The returned ids can be used to {@link reconstructToCode|reconstruct the slice to R code}.
17
17
  *
18
18
  * @param graph - The dataflow graph to conduct the slicing on.
19
- * @param ast - The normalized AST of the code (used to get static depth information of the lexemes in case of control flow dependencies that may have no effect on the slicing scope).
19
+ * @param ast - The normalized AST of the code (used to get static nesting information of the lexemes in case of control flow dependencies that may have no effect on the slicing scope).
20
20
  * @param criteria - The criteras to slice on.
21
21
  * @param threshold - The maximum number of nodes to visit in the graph. If the threshold is reached, the slice will side with inclusion and drop its minimal guarantee. The limit ensures that the algorithm halts.
22
22
  */
@@ -25,7 +25,7 @@ function staticSlicing(graph, { idMap }, criteria, threshold = 75) {
25
25
  const decodedCriteria = (0, parse_1.convertAllSlicingCriteriaToIds)(criteria, idMap);
26
26
  (0, log_1.expensiveTrace)(exports.slicerLogger, () => `calculating slice for ${decodedCriteria.length} seed criteria: ${decodedCriteria.map(s => JSON.stringify(s)).join(', ')}`);
27
27
  const queue = new visiting_queue_1.VisitingQueue(threshold);
28
- let minDepth = Number.MAX_SAFE_INTEGER;
28
+ let minNesting = Number.MAX_SAFE_INTEGER;
29
29
  const sliceSeedIds = new Set();
30
30
  // every node ships the call environment which registers the calling environment
31
31
  {
@@ -33,8 +33,8 @@ function staticSlicing(graph, { idMap }, criteria, threshold = 75) {
33
33
  const basePrint = (0, fingerprint_1.envFingerprint)(emptyEnv);
34
34
  for (const { id: startId } of decodedCriteria) {
35
35
  queue.add(startId, emptyEnv, basePrint, false);
36
- // retrieve the minimum depth of all nodes to only add control dependencies if they are "part" of the current execution
37
- minDepth = Math.min(minDepth, idMap.get(startId)?.info.depth ?? minDepth);
36
+ // retrieve the minimum nesting of all nodes to only add control dependencies if they are "part" of the current execution
37
+ minNesting = Math.min(minNesting, idMap.get(startId)?.info.nesting ?? minNesting);
38
38
  sliceSeedIds.add(startId);
39
39
  }
40
40
  /* additionally,
@@ -54,11 +54,11 @@ function staticSlicing(graph, { idMap }, criteria, threshold = 75) {
54
54
  continue;
55
55
  }
56
56
  const [currentVertex, currentEdges] = currentInfo;
57
- // we only add control dependencies iff 1) we are in different function call or 2) they have, at least, the same depth as the slicing seed
57
+ // we only add control dependencies iff 1) we are in different function call or 2) they have, at least, the same nesting as the slicing seed
58
58
  if (currentVertex.controlDependencies && currentVertex.controlDependencies.length > 0) {
59
59
  const topLevel = graph.isRoot(id) || sliceSeedIds.has(id);
60
60
  for (const cd of currentVertex.controlDependencies.filter(({ id }) => !queue.hasId(id))) {
61
- if (!topLevel || (idMap.get(cd.id)?.info.depth ?? 0) <= minDepth) {
61
+ if (!topLevel || (idMap.get(cd.id)?.info.nesting ?? 0) >= minNesting) {
62
62
  queue.add(cd.id, baseEnvironment, baseEnvFingerprint, false);
63
63
  }
64
64
  }
package/util/version.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.flowrVersion = void 0;
4
4
  const semver_1 = require("semver");
5
5
  // this is automatically replaced with the current version by release-it
6
- const version = '2.0.20';
6
+ const version = '2.0.22';
7
7
  function flowrVersion() {
8
8
  return new semver_1.SemVer(version);
9
9
  }