@eagleoutice/flowr 2.2.11 → 2.2.12

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 (97) hide show
  1. package/README.md +4 -4
  2. package/benchmark/slicer.d.ts +49 -22
  3. package/benchmark/slicer.js +88 -28
  4. package/benchmark/stats/print.js +16 -10
  5. package/benchmark/stats/size-of.js +18 -1
  6. package/benchmark/stats/stats.d.ts +3 -0
  7. package/benchmark/summarizer/second-phase/process.js +8 -2
  8. package/cli/benchmark-app.d.ts +5 -0
  9. package/cli/benchmark-app.js +49 -6
  10. package/cli/benchmark-helper-app.d.ts +4 -0
  11. package/cli/benchmark-helper-app.js +20 -4
  12. package/cli/common/options.js +13 -4
  13. package/cli/repl/commands/repl-commands.js +2 -0
  14. package/cli/repl/commands/repl-dataflow.d.ts +2 -0
  15. package/cli/repl/commands/repl-dataflow.js +35 -1
  16. package/config.d.ts +18 -2
  17. package/config.js +24 -4
  18. package/dataflow/environments/built-in-config.d.ts +5 -2
  19. package/dataflow/environments/built-in-config.js +8 -2
  20. package/dataflow/environments/built-in.d.ts +8 -1
  21. package/dataflow/environments/built-in.js +8 -1
  22. package/dataflow/environments/clone.d.ts +5 -0
  23. package/dataflow/environments/clone.js +5 -0
  24. package/dataflow/environments/default-builtin-config.js +93 -9
  25. package/dataflow/environments/define.d.ts +5 -1
  26. package/dataflow/environments/define.js +36 -10
  27. package/dataflow/environments/overwrite.js +4 -0
  28. package/dataflow/environments/remove.d.ts +6 -0
  29. package/dataflow/environments/remove.js +24 -0
  30. package/dataflow/environments/resolve-by-name.js +1 -1
  31. package/dataflow/graph/dataflowgraph-builder.d.ts +76 -6
  32. package/dataflow/graph/dataflowgraph-builder.js +102 -6
  33. package/dataflow/graph/graph.d.ts +6 -1
  34. package/dataflow/graph/graph.js +24 -0
  35. package/dataflow/graph/vertex.d.ts +42 -2
  36. package/dataflow/graph/vertex.js +32 -0
  37. package/dataflow/internal/linker.js +3 -1
  38. package/dataflow/internal/process/functions/call/built-in/built-in-access.d.ts +1 -0
  39. package/dataflow/internal/process/functions/call/built-in/built-in-access.js +55 -45
  40. package/dataflow/internal/process/functions/call/built-in/built-in-apply.d.ts +6 -4
  41. package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +27 -8
  42. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +37 -7
  43. package/dataflow/internal/process/functions/call/built-in/built-in-eval.d.ts +10 -0
  44. package/dataflow/internal/process/functions/call/built-in/built-in-eval.js +140 -0
  45. package/dataflow/internal/process/functions/call/built-in/built-in-list.js +51 -17
  46. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.d.ts +3 -0
  47. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +83 -29
  48. package/dataflow/internal/process/functions/call/built-in/built-in-rm.d.ts +7 -0
  49. package/dataflow/internal/process/functions/call/built-in/built-in-rm.js +41 -0
  50. package/dataflow/internal/process/functions/call/built-in/built-in-source.js +17 -5
  51. package/dataflow/internal/process/functions/call/built-in/built-in-vector.d.ts +15 -0
  52. package/dataflow/internal/process/functions/call/built-in/built-in-vector.js +75 -0
  53. package/dataflow/internal/process/functions/call/common.d.ts +1 -1
  54. package/dataflow/internal/process/functions/call/common.js +4 -2
  55. package/dataflow/internal/process/functions/call/named-call-handling.d.ts +2 -0
  56. package/dataflow/internal/process/functions/call/named-call-handling.js +9 -5
  57. package/dataflow/internal/process/process-named-call.d.ts +3 -0
  58. package/dataflow/internal/process/process-named-call.js +3 -0
  59. package/documentation/doc-util/doc-cfg.d.ts +11 -2
  60. package/documentation/doc-util/doc-cfg.js +35 -6
  61. package/documentation/doc-util/doc-code.js +10 -2
  62. package/documentation/print-capabilities-markdown.js +1 -1
  63. package/documentation/print-cfg-wiki.d.ts +1 -0
  64. package/documentation/print-cfg-wiki.js +84 -0
  65. package/documentation/print-core-wiki.js +2 -2
  66. package/documentation/print-interface-wiki.js +1 -0
  67. package/documentation/print-query-wiki.js +2 -2
  68. package/package.json +2 -1
  69. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +1 -1
  70. package/queries/catalog/dependencies-query/dependencies-query-executor.js +13 -5
  71. package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +1 -25
  72. package/queries/catalog/dependencies-query/dependencies-query-format.js +2 -145
  73. package/queries/catalog/dependencies-query/function-info/function-info.d.ts +24 -0
  74. package/queries/catalog/dependencies-query/function-info/function-info.js +10 -0
  75. package/queries/catalog/dependencies-query/function-info/library-functions.d.ts +2 -0
  76. package/queries/catalog/dependencies-query/function-info/library-functions.js +18 -0
  77. package/queries/catalog/dependencies-query/function-info/read-functions.d.ts +2 -0
  78. package/queries/catalog/dependencies-query/function-info/read-functions.js +101 -0
  79. package/queries/catalog/dependencies-query/function-info/source-functions.d.ts +2 -0
  80. package/queries/catalog/dependencies-query/function-info/source-functions.js +11 -0
  81. package/queries/catalog/dependencies-query/function-info/write-functions.d.ts +2 -0
  82. package/queries/catalog/dependencies-query/function-info/write-functions.js +87 -0
  83. package/r-bridge/data/data.d.ts +2 -2
  84. package/r-bridge/data/data.js +2 -2
  85. package/util/arrays.d.ts +23 -0
  86. package/util/arrays.js +41 -0
  87. package/util/cfg/visitor.d.ts +1 -1
  88. package/util/cfg/visitor.js +2 -2
  89. package/util/{list-access.d.ts → containers.d.ts} +24 -4
  90. package/util/{list-access.js → containers.js} +42 -12
  91. package/util/mermaid/ast.js +12 -1
  92. package/util/mermaid/cfg.js +2 -2
  93. package/util/parallel.d.ts +2 -1
  94. package/util/parallel.js +11 -2
  95. package/util/prefix.d.ts +13 -0
  96. package/util/prefix.js +34 -0
  97. package/util/version.js +1 -1
@@ -6,16 +6,15 @@ const known_call_handling_1 = require("../known-call-handling");
6
6
  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
- const assert_1 = require("../../../../../../util/assert");
10
9
  const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
11
10
  const logger_1 = require("../../../../../logger");
12
- const vertex_1 = require("../../../../../graph/vertex");
13
11
  const graph_1 = require("../../../../../graph/graph");
14
12
  const edge_1 = require("../../../../../graph/edge");
15
- const dfg_1 = require("../../../../../../util/mermaid/dfg");
16
13
  const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
17
- const list_access_1 = require("../../../../../../util/list-access");
14
+ const containers_1 = require("../../../../../../util/containers");
18
15
  const config_1 = require("../../../../../../config");
16
+ const unpack_argument_1 = require("../argument/unpack-argument");
17
+ const built_in_access_1 = require("./built-in-access");
19
18
  function processReplacementFunction(name,
20
19
  /** The last one has to be the value */
21
20
  args, rootId, data, config) {
@@ -25,45 +24,38 @@ args, rootId, data, config) {
25
24
  }
26
25
  /* we only get here if <-, <<-, ... or whatever is part of the replacement is not overwritten */
27
26
  (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Replacement ${name.content} with ${JSON.stringify(args)}, processing`);
28
- let indices = undefined;
29
- if (name.content === '$<-' && (0, config_1.getConfig)().solver.pointerTracking) {
30
- const nonEmptyArgs = args.filter(arg => arg !== r_function_call_1.EmptyArgument);
31
- const accessedArg = nonEmptyArgs.find(arg => arg.info.role === "accessed" /* RoleInParent.Accessed */);
32
- const accessArg = nonEmptyArgs.find(arg => arg.info.role === "index-access" /* RoleInParent.IndexAccess */);
33
- if (accessArg !== undefined && accessedArg != undefined) {
34
- const leafIndex = { lexeme: accessArg.lexeme, nodeId: accessedArg.info.parent ?? '' };
35
- const accessIndices = {
36
- indices: [leafIndex],
37
- isContainer: false
38
- };
39
- // Check for nested access
40
- if (accessedArg.value?.type === type_1.RType.Access) {
41
- indices = (0, list_access_1.constructNestedAccess)(accessedArg.value, accessIndices);
42
- }
43
- else {
44
- // use access node as reference to get complete line in slice
45
- indices = [accessIndices];
46
- }
47
- }
27
+ let indices = config.activeIndices;
28
+ if ((0, config_1.getConfig)().solver.pointerTracking) {
29
+ indices ??= constructAccessedIndices(name.content, args);
48
30
  }
49
31
  /* 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 */
50
32
  const res = (0, built_in_assignment_1.processAssignment)(name, [args[0], args[args.length - 1]], rootId, data, {
51
33
  superAssignment: config.assignmentOperator === '<<-',
52
34
  makeMaybe: indices !== undefined ? false : config.makeMaybe,
53
- indicesCollection: indices
35
+ indicesCollection: indices,
36
+ canBeReplacement: true
54
37
  });
38
+ const convertedArgs = config.readIndices ? args.slice(1, -1) : (0, built_in_access_1.symbolArgumentsToStrings)(args.slice(1, -1), 0);
55
39
  /* now, we soft-inject other arguments, so that calls like `x[y] <- 3` are linked correctly */
56
40
  const { callArgs } = (0, common_1.processAllArguments)({
57
41
  functionName: (0, info_1.initializeCleanDataflowInformation)(rootId, data),
58
- args: args.slice(1, -1),
42
+ args: convertedArgs,
59
43
  data,
60
44
  functionRootId: rootId,
61
45
  finalGraph: res.graph,
62
46
  forceArgs: config.forceArgs,
63
47
  });
64
- const fn = res.graph.getVertex(rootId, true);
65
- (0, assert_1.guard)(fn?.tag === vertex_1.VertexType.FunctionCall && fn.args.length === 2, () => `Function ${rootId} not found in graph or not 2-arg fn-call (${JSON.stringify(fn)}) ${(0, dfg_1.graphToMermaidUrl)(res.graph)}`);
66
- fn.args = [fn.args[0], ...callArgs, fn.args[1]];
48
+ (0, common_1.patchFunctionCall)({
49
+ nextGraph: res.graph,
50
+ data,
51
+ rootId,
52
+ name,
53
+ argumentProcessResult: args.map(a => a === r_function_call_1.EmptyArgument ? undefined : { entryPoint: (0, unpack_argument_1.unpackArgument)(a)?.info.id })
54
+ });
55
+ const firstArg = (0, unpack_argument_1.unpackArgument)(args[0])?.info.id;
56
+ if (firstArg) {
57
+ res.graph.addEdge(firstArg, rootId, edge_1.EdgeType.DefinedBy | edge_1.EdgeType.Reads);
58
+ }
67
59
  /* a replacement reads all of its call args as well, at least as far as I am aware of */
68
60
  for (const arg of callArgs) {
69
61
  const ref = (0, graph_1.getReferenceOfArgument)(arg);
@@ -73,4 +65,66 @@ args, rootId, data, config) {
73
65
  }
74
66
  return res;
75
67
  }
68
+ /**
69
+ * Constructs accessed indices of replacement function recursively.
70
+ *
71
+ * Example:
72
+ * ```r
73
+ * a$b <- 1
74
+ * # results in index with lexeme b as identifier
75
+ *
76
+ * a[[1]]$b
77
+ * # results in index with index 1 as identifier with a sub-index with lexeme b as identifier
78
+ * ```
79
+ *
80
+ * @param operation - Operation of replacement function e.g. '$\<-', '[\<-', '[[\<-'
81
+ * @param args - Arguments of the replacement function
82
+ * @returns Accessed indices construct
83
+ */
84
+ function constructAccessedIndices(operation, args) {
85
+ const { accessedArg, accessArg } = (0, containers_1.getAccessOperands)(args);
86
+ if (accessedArg === undefined || accessArg?.value === undefined || !isSupportedOperation(operation, accessArg.value)) {
87
+ return undefined;
88
+ }
89
+ const constructIdentifier = getIdentifierBuilder(operation);
90
+ const leafIndex = {
91
+ identifier: constructIdentifier(accessArg),
92
+ nodeId: accessedArg.info.parent ?? ''
93
+ };
94
+ const accessIndices = {
95
+ indices: [leafIndex],
96
+ isContainer: false
97
+ };
98
+ // Check for nested access
99
+ let indicesCollection = undefined;
100
+ if (accessedArg.value?.type === type_1.RType.Access) {
101
+ indicesCollection = (0, containers_1.constructNestedAccess)(accessedArg.value, accessIndices, constructIdentifier);
102
+ }
103
+ else {
104
+ // use access node as reference to get complete line in slice
105
+ indicesCollection = [accessIndices];
106
+ }
107
+ return indicesCollection;
108
+ }
109
+ function isSupportedOperation(operation, value) {
110
+ const isNameBasedAccess = (operation === '$<-' || operation === '@<-') && value.type === type_1.RType.Symbol;
111
+ const isNumericalIndexBasedAccess = (operation === '[[<-' || operation === '[<-') && value.type === type_1.RType.Number;
112
+ return isNameBasedAccess || isNumericalIndexBasedAccess;
113
+ }
114
+ function getIdentifierBuilder(operation) {
115
+ if (operation === '$<-' || operation == '@<-') {
116
+ return (arg) => {
117
+ return {
118
+ index: undefined,
119
+ lexeme: arg.lexeme,
120
+ };
121
+ };
122
+ }
123
+ // [[<- and [<-
124
+ return (arg) => {
125
+ return {
126
+ index: Number(arg.lexeme),
127
+ };
128
+ };
129
+ }
76
130
  //# sourceMappingURL=built-in-replacement.js.map
@@ -0,0 +1,7 @@
1
+ import type { DataflowProcessorInformation } from '../../../../../processor';
2
+ import type { DataflowInformation } from '../../../../../info';
3
+ import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
4
+ import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
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
+ export declare function processRm<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processRm = processRm;
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
+ const remove_1 = require("../../../../../environments/remove");
8
+ const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
9
+ function processRm(name, args, rootId, data) {
10
+ if (args.length === 0) {
11
+ logger_1.dataflowLogger.warn('empty rm, skipping');
12
+ return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
13
+ }
14
+ const res = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
15
+ const names = [];
16
+ for (const arg of args) {
17
+ if (arg === r_function_call_1.EmptyArgument) {
18
+ logger_1.dataflowLogger.warn('empty argument in rm, skipping');
19
+ continue;
20
+ }
21
+ const unpacked = arg.value;
22
+ if (unpacked === undefined || (unpacked.type !== type_1.RType.Symbol && unpacked.type !== type_1.RType.String)) {
23
+ logger_1.dataflowLogger.warn(`argument is not a symbol or string, skipping ${JSON.stringify(unpacked)}`);
24
+ }
25
+ else if (unpacked.type === type_1.RType.Symbol) {
26
+ names.push(unpacked.content);
27
+ }
28
+ else if (unpacked.type === type_1.RType.String) {
29
+ names.push(unpacked.content.str);
30
+ }
31
+ }
32
+ let env = res.environment;
33
+ for (const name of names) {
34
+ env = (0, remove_1.remove)(name, env);
35
+ }
36
+ return {
37
+ ...res,
38
+ environment: env
39
+ };
40
+ }
41
+ //# sourceMappingURL=built-in-rm.js.map
@@ -103,6 +103,10 @@ function findSource(seed, data) {
103
103
  return found;
104
104
  }
105
105
  function processSourceCall(name, args, rootId, data, config) {
106
+ if (args.length !== 1) {
107
+ logger_1.dataflowLogger.warn(`Expected exactly one argument for source currently, but got ${args.length} instead, skipping`);
108
+ return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
109
+ }
106
110
  const information = config.includeFunctionCall ?
107
111
  (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information
108
112
  : (0, info_1.initializeCleanDataflowInformation)(rootId, data);
@@ -135,12 +139,14 @@ function processSourceCall(name, args, rootId, data, config) {
135
139
  if (filepath !== undefined) {
136
140
  const request = sourceProvider.createRequest(filepath);
137
141
  // check if the sourced file has already been dataflow analyzed, and if so, skip it
138
- if (data.referenceChain.find(e => e.request === request.request && e.content === request.content)) {
139
- logger_1.dataflowLogger.warn(`Found loop in dataflow analysis for ${JSON.stringify(request)}: ${JSON.stringify(data.referenceChain)}, skipping further dataflow analysis`);
142
+ const limit = (0, config_1.getConfig)().solver.resolveSource?.repeatedSourceLimit ?? 0;
143
+ const findCount = data.referenceChain.filter(e => e.request === request.request && e.content === request.content).length;
144
+ if (findCount > limit) {
145
+ logger_1.dataflowLogger.warn(`Found cycle (>=${limit + 1}) in dataflow analysis for ${JSON.stringify(request)}: ${JSON.stringify(data.referenceChain)}, skipping further dataflow analysis`);
140
146
  information.graph.markIdForUnknownSideEffects(rootId);
141
147
  return information;
142
148
  }
143
- return sourceRequest(rootId, request, data, information, (0, decorate_1.sourcedDeterministicCountingIdGenerator)(path, name.location));
149
+ return sourceRequest(rootId, request, data, information, (0, decorate_1.sourcedDeterministicCountingIdGenerator)((findCount > 0 ? findCount + '::' : '') + path, name.location));
144
150
  }
145
151
  }
146
152
  (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Non-constant argument ${JSON.stringify(sourceFile)} for source is currently not supported, skipping`);
@@ -193,7 +199,13 @@ function sourceRequest(rootId, request, data, information, getId) {
193
199
  for (const [k, v] of normalized.idMap) {
194
200
  data.completeAst.idMap.set(k, v);
195
201
  }
196
- return newInformation;
202
+ return {
203
+ ...newInformation,
204
+ in: newInformation.in.concat(dataflow.in),
205
+ out: newInformation.out.concat(dataflow.out),
206
+ unknownReferences: newInformation.unknownReferences.concat(dataflow.unknownReferences),
207
+ exitPoints: dataflow.exitPoints
208
+ };
197
209
  }
198
210
  function standaloneSourceFile(inputRequest, data, uniqueSourceId, information) {
199
211
  const path = inputRequest.request === 'file' ? inputRequest.content : '-inline-';
@@ -210,6 +222,6 @@ function standaloneSourceFile(inputRequest, data, uniqueSourceId, information) {
210
222
  currentRequest: request,
211
223
  environment: information.environment,
212
224
  referenceChain: [...data.referenceChain, inputRequest]
213
- }, information, (0, decorate_1.deterministicPrefixIdGenerator)(path + '@' + uniqueSourceId));
225
+ }, information, (0, decorate_1.deterministicPrefixIdGenerator)(path + '::' + uniqueSourceId));
214
226
  }
215
227
  //# sourceMappingURL=built-in-source.js.map
@@ -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 vector call.
9
+ *
10
+ * Example:
11
+ * ```r
12
+ * c(1, 2, 3, 4)
13
+ * ```
14
+ */
15
+ export declare function processVector<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processVector = processVector;
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 known_call_handling_1 = require("../known-call-handling");
7
+ const config_1 = require("../../../../../../config");
8
+ const containers_1 = require("../../../../../../util/containers");
9
+ /**
10
+ * Process a vector call.
11
+ *
12
+ * Example:
13
+ * ```r
14
+ * c(1, 2, 3, 4)
15
+ * ```
16
+ */
17
+ function processVector(name, args, rootId, data) {
18
+ const fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data });
19
+ if (!(0, config_1.getConfig)().solver.pointerTracking) {
20
+ return fnCall.information;
21
+ }
22
+ const vectorArgs = [];
23
+ let argIndex = 1;
24
+ for (const arg of args) {
25
+ // Skip invalid argument types
26
+ if (arg === r_function_call_1.EmptyArgument || arg.type !== type_1.RType.Argument || arg.value === undefined) {
27
+ continue;
28
+ }
29
+ if (isPrimitive(arg.value.type)) {
30
+ vectorArgs.push({
31
+ identifier: { index: argIndex++ },
32
+ nodeId: arg.value.info.id,
33
+ });
34
+ }
35
+ else {
36
+ // Check whether argument value can be resolved
37
+ let indicesCollection;
38
+ if (arg.value.type === type_1.RType.Symbol) {
39
+ indicesCollection = (0, containers_1.resolveIndicesByName)(arg.value.lexeme, data.environment);
40
+ }
41
+ else {
42
+ // Check whether argument is nested container
43
+ indicesCollection = fnCall.information.graph.getVertex(arg.value.info.id)?.indicesCollection;
44
+ }
45
+ const flattenedIndices = indicesCollection?.flatMap(indices => indices.indices)
46
+ .map(index => {
47
+ return {
48
+ identifier: { index: argIndex++ },
49
+ nodeId: index.nodeId,
50
+ };
51
+ }) ?? [];
52
+ vectorArgs.push(...flattenedIndices);
53
+ }
54
+ }
55
+ if ((0, config_1.isOverPointerAnalysisThreshold)(vectorArgs.length)) {
56
+ return fnCall.information;
57
+ }
58
+ const indices = {
59
+ indices: vectorArgs,
60
+ isContainer: true,
61
+ };
62
+ // Add resolved indices to vertex
63
+ const vertex = fnCall.information.graph.getVertex(rootId);
64
+ if (vertex) {
65
+ vertex.indicesCollection = [indices];
66
+ }
67
+ return fnCall.information;
68
+ }
69
+ /**
70
+ * Checks whether the passed type is primitive i.e. number, logical or string.
71
+ */
72
+ function isPrimitive(type) {
73
+ return type === type_1.RType.Number || type === type_1.RType.Logical || type === type_1.RType.String;
74
+ }
75
+ //# sourceMappingURL=built-in-vector.js.map
@@ -34,6 +34,6 @@ export interface PatchFunctionCallInput<OtherInfo> {
34
34
  readonly rootId: NodeId;
35
35
  readonly name: RSymbol<OtherInfo & ParentInformation>;
36
36
  readonly data: DataflowProcessorInformation<OtherInfo & ParentInformation>;
37
- readonly argumentProcessResult: readonly (DataflowInformation | undefined)[];
37
+ readonly argumentProcessResult: readonly (Pick<DataflowInformation, 'entryPoint'> | undefined)[];
38
38
  }
39
39
  export declare function patchFunctionCall<OtherInfo>({ nextGraph, rootId, name, data, argumentProcessResult }: PatchFunctionCallInput<OtherInfo>): void;
@@ -64,9 +64,12 @@ function processAllArguments({ functionName, args, data, finalGraph, functionRoo
64
64
  }
65
65
  processedArguments.push(processed);
66
66
  finalEnv = (0, overwrite_1.overwriteEnvironment)(finalEnv, processed.environment);
67
+ finalGraph.mergeWith(processed.graph);
67
68
  // resolve reads within argument, we resolve before adding the `processed.environment` to avoid cyclic dependencies
68
69
  for (const ingoing of [...processed.in, ...processed.unknownReferences]) {
69
- const tryToResolve = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, argEnv, identifier_1.ReferenceType.Unknown) : undefined;
70
+ // check if it is called directly
71
+ const vtx = finalGraph.getVertex(ingoing.nodeId);
72
+ const tryToResolve = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, argEnv, vtx?.tag === vertex_1.VertexType.FunctionCall ? identifier_1.ReferenceType.Function : identifier_1.ReferenceType.Unknown) : undefined;
70
73
  if (tryToResolve === undefined) {
71
74
  remainingReadInArgs.push(ingoing);
72
75
  }
@@ -90,7 +93,6 @@ function processAllArguments({ functionName, args, data, finalGraph, functionRoo
90
93
  }
91
94
  }
92
95
  argEnv = (0, overwrite_1.overwriteEnvironment)(argEnv, processed.environment);
93
- finalGraph.mergeWith(processed.graph);
94
96
  if (arg.type !== type_1.RType.Argument || !arg.name) {
95
97
  callArgs.push({ nodeId: processed.entryPoint, controlDependencies: undefined, type: identifier_1.ReferenceType.Argument });
96
98
  }
@@ -4,4 +4,6 @@ import type { ParentInformation } from '../../../../../r-bridge/lang-4.x/ast/mod
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 { DataflowGraph } from '../../../../graph/graph';
8
+ export declare function markAsOnlyBuiltIn(graph: DataflowGraph, rootId: NodeId): void;
7
9
  export declare function processNamedCall<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.markAsOnlyBuiltIn = markAsOnlyBuiltIn;
3
4
  exports.processNamedCall = processNamedCall;
4
5
  const info_1 = require("../../../../info");
5
6
  const known_call_handling_1 = require("./known-call-handling");
@@ -27,6 +28,13 @@ function processDefaultFunctionProcessor(information, name, args, rootId, data)
27
28
  const call = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: (resolve?.length ?? 0) > 0 ? undefined : 'all' });
28
29
  return mergeInformation(information, call.information);
29
30
  }
31
+ function markAsOnlyBuiltIn(graph, rootId) {
32
+ const v = graph.getVertex(rootId);
33
+ if (v?.tag === vertex_1.VertexType.FunctionCall) {
34
+ v.onlyBuiltin = true;
35
+ v.environment = undefined;
36
+ }
37
+ }
30
38
  function processNamedCall(name, args, rootId, data) {
31
39
  const resolved = (0, resolve_by_name_1.resolveByName)(name.content, data.environment, identifier_1.ReferenceType.Function) ?? [];
32
40
  let defaultProcessor = resolved.length === 0;
@@ -46,11 +54,7 @@ function processNamedCall(name, args, rootId, data) {
46
54
  }
47
55
  else if (information && builtIn) {
48
56
  // mark the function call as built in only
49
- const v = information.graph.getVertex(rootId);
50
- if (v?.tag === vertex_1.VertexType.FunctionCall) {
51
- v.onlyBuiltin = true;
52
- v.environment = undefined;
53
- }
57
+ markAsOnlyBuiltIn(information.graph, rootId);
54
58
  }
55
59
  return information ?? (0, info_1.initializeCleanDataflowInformation)(rootId, data);
56
60
  }
@@ -3,4 +3,7 @@ import type { DataflowInformation } from '../../info';
3
3
  import type { Base, RNode, Location } from '../../../r-bridge/lang-4.x/ast/model/model';
4
4
  import type { ParentInformation } from '../../../r-bridge/lang-4.x/ast/model/processing/decorate';
5
5
  import type { EmptyArgument } from '../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
6
+ /**
7
+ * Helper function for {@link processNamedCall} using the given `functionName` as the name of the function.
8
+ */
6
9
  export declare function processAsNamedCall<OtherInfo>(functionName: RNode<OtherInfo & ParentInformation> & Base<OtherInfo> & Location, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, name: string, args: readonly (RNode<OtherInfo & ParentInformation> | typeof EmptyArgument | undefined)[]): DataflowInformation;
@@ -4,6 +4,9 @@ exports.processAsNamedCall = processAsNamedCall;
4
4
  const named_call_handling_1 = require("./functions/call/named-call-handling");
5
5
  const make_argument_1 = require("./functions/call/argument/make-argument");
6
6
  const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type");
7
+ /**
8
+ * Helper function for {@link processNamedCall} using the given `functionName` as the name of the function.
9
+ */
7
10
  function processAsNamedCall(functionName, data, name, args) {
8
11
  return (0, named_call_handling_1.processNamedCall)({
9
12
  type: type_1.RType.Symbol,
@@ -1,7 +1,16 @@
1
1
  import type { ControlFlowInformation } from '../../util/cfg/cfg';
2
- import type { RShell } from '../../r-bridge/shell';
3
2
  import type { NormalizedAst } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
4
- export declare function getCfg(shell: RShell, code: string): Promise<{
3
+ import type { KnownParser } from '../../r-bridge/parser';
4
+ import type { DataflowInformation } from '../../dataflow/info';
5
+ export declare function getCfg(parser: KnownParser, code: string): Promise<{
5
6
  info: ControlFlowInformation;
6
7
  ast: NormalizedAst;
8
+ dataflow: DataflowInformation;
7
9
  }>;
10
+ export declare function printCfg(cfg: ControlFlowInformation, ast: NormalizedAst, prefix?: string): string;
11
+ export interface PrintCfgOptions {
12
+ readonly showCode?: boolean;
13
+ readonly openCode?: boolean;
14
+ readonly prefix?: string;
15
+ }
16
+ export declare function printCFGCode(parser: KnownParser, code: string, { showCode, openCode, prefix }?: PrintCfgOptions): Promise<string>;
@@ -1,18 +1,47 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCfg = getCfg;
4
+ exports.printCfg = printCfg;
5
+ exports.printCFGCode = printCFGCode;
4
6
  const cfg_1 = require("../../util/cfg/cfg");
5
- const pipeline_executor_1 = require("../../core/pipeline-executor");
6
7
  const default_pipelines_1 = require("../../core/steps/pipeline/default-pipelines");
7
8
  const retriever_1 = require("../../r-bridge/retriever");
8
- async function getCfg(shell, code) {
9
- const steps = await new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_DATAFLOW_PIPELINE, {
10
- parser: shell,
9
+ const time_1 = require("../../util/time");
10
+ const doc_files_1 = require("./doc-files");
11
+ const cfg_2 = require("../../util/mermaid/cfg");
12
+ const doc_code_1 = require("./doc-code");
13
+ async function getCfg(parser, code) {
14
+ const result = await (0, default_pipelines_1.createDataflowPipeline)(parser, {
11
15
  request: (0, retriever_1.requestFromInput)(code)
12
16
  }).allRemainingSteps();
17
+ const cfg = (0, cfg_1.extractCFG)(result.normalize, result.dataflow.graph);
13
18
  return {
14
- info: (0, cfg_1.extractCFG)(steps.normalize, steps.dataflow?.graph),
15
- ast: steps.normalize
19
+ info: cfg,
20
+ ast: result.normalize,
21
+ dataflow: result.dataflow
16
22
  };
17
23
  }
24
+ function printCfg(cfg, ast, prefix = 'flowchart TD\n') {
25
+ return `
26
+ ${(0, doc_code_1.codeBlock)('mermaid', (0, cfg_2.cfgToMermaid)(cfg, ast, prefix))}
27
+ `;
28
+ }
29
+ async function printCFGCode(parser, code, { showCode = true, openCode = false, prefix = 'flowchart BT\n' } = {}) {
30
+ const now = performance.now();
31
+ const res = await getCfg(parser, code);
32
+ const duration = performance.now() - now;
33
+ const metaInfo = `The analysis required _${(0, time_1.printAsMs)(duration)}_ (including the dataflow analysis, normalization, and parsing with the [${parser.name}](${doc_files_1.FlowrWikiBaseRef}/Engines) engine) within the generation environment.`;
34
+ return '\n\n' + printCfg(res.info, res.ast, prefix) + (showCode ? `
35
+ <details${openCode ? ' open' : ''}>
36
+
37
+ <summary style="color:gray">R Code of the CFG</summary>
38
+
39
+ ${metaInfo}
40
+
41
+ ${(0, doc_code_1.codeBlock)('r', code)}
42
+
43
+ </details>
44
+
45
+ ` : '\n(' + metaInfo + ')\n\n');
46
+ }
18
47
  //# sourceMappingURL=doc-cfg.js.map
@@ -4,17 +4,25 @@ exports.codeBlock = codeBlock;
4
4
  exports.codeInline = codeInline;
5
5
  exports.jsonWithLimit = jsonWithLimit;
6
6
  const json_1 = require("../../util/json");
7
+ const environment_1 = require("../../dataflow/environments/environment");
7
8
  function codeBlock(language, code) {
8
9
  return `\n\`\`\`${language}\n${code?.trim() ?? ''}\n\`\`\`\n`;
9
10
  }
10
11
  function codeInline(code) {
11
12
  return `<code>${code}</code>`;
12
13
  }
13
- function jsonWithLimit(object, maxLength = 5_000, tooLongText = '_As the code is pretty long, we inhibit pretty printing and syntax highlighting (JSON):_') {
14
+ function jsonWithLimit(object, maxLength = 5_000, tooLongText = '_As the code is pretty long, we inhibit pretty printing and syntax highlighting (JSON, hiding built-in):_') {
14
15
  const prettyPrinted = JSON.stringify(object, json_1.jsonReplacer, 2);
15
16
  return `
16
17
  ${prettyPrinted.length > maxLength ? tooLongText : ''}
17
- ${codeBlock(prettyPrinted.length > maxLength ? 'text' : 'json', prettyPrinted.length > 5_000 ? JSON.stringify(object, json_1.jsonReplacer) : prettyPrinted)}
18
+ ${codeBlock(prettyPrinted.length > maxLength ? 'text' : 'json', prettyPrinted.length > 5_000 ? JSON.stringify(object, (k, v) => {
19
+ if (typeof v === 'object' && v !== null && 'id' in v && v['id'] === 0 && 'memory' in v && v['memory']) {
20
+ return '<BuiltInEnvironment>';
21
+ }
22
+ else {
23
+ return (0, environment_1.builtInEnvJsonReplacer)(k, v);
24
+ }
25
+ }) : prettyPrinted)}
18
26
  `;
19
27
  }
20
28
  //# sourceMappingURL=doc-code.js.map
@@ -86,7 +86,7 @@ async function printSingleCapability(info, depth, index, capability) {
86
86
  if (capability.url) {
87
87
  nextLine += '\\\nSee ' + (0, strings_1.joinWithLast)(capability.url.map(({ name, href }) => `[${name}](${href})`)) + ' for more info.';
88
88
  }
89
- nextLine += ' Internal ID: `' + capability.id + '`';
89
+ nextLine += ' (internal ID: `' + capability.id + '`)';
90
90
  if (capability.example) {
91
91
  nextLine += `\n${(0, doc_general_1.prefixLines)(typeof capability.example === 'string' ? capability.example : await capability.example(info.parser), nextLineIndent + '> ')}`;
92
92
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const shell_1 = require("../r-bridge/shell");
7
+ const log_1 = require("../../test/functionality/_helper/log");
8
+ const doc_auto_gen_1 = require("./doc-util/doc-auto-gen");
9
+ const doc_code_1 = require("./doc-util/doc-code");
10
+ const doc_types_1 = require("./doc-util/doc-types");
11
+ const path_1 = __importDefault(require("path"));
12
+ const doc_files_1 = require("./doc-util/doc-files");
13
+ const doc_cli_option_1 = require("./doc-util/doc-cli-option");
14
+ const doc_structure_1 = require("./doc-util/doc-structure");
15
+ const doc_issue_1 = require("./doc-util/doc-issue");
16
+ const doc_cfg_1 = require("./doc-util/doc-cfg");
17
+ const visitor_1 = require("../util/cfg/visitor");
18
+ async function getText(shell) {
19
+ const rversion = (await shell.usedRVersion())?.format() ?? 'unknown';
20
+ const types = (0, doc_types_1.getTypesFromFolderAsMermaid)({
21
+ rootFolder: path_1.default.resolve('./src'),
22
+ typeName: 'RNode',
23
+ inlineTypes: doc_types_1.mermaidHide
24
+ });
25
+ return `${(0, doc_auto_gen_1.autoGenHeader)({ filename: module.filename, purpose: 'control flow graph', rVersion: rversion })}
26
+
27
+ _flowR_ produces two main perspectives of the program: 1) a [normalized version of the AST](${doc_files_1.FlowrWikiBaseRef}/Normalized-AST)
28
+ and 2) a [dataflow graph](${doc_files_1.FlowrWikiBaseRef}/Dataflow%20Graph). However, for further analyses, we also provide an explicit control flow graph
29
+ that is calculated from the normalized AST **and** the dataflow graph to incorporate change in language semantics.
30
+ flowR also uses this CFG for some of its queries (e.g., to link to the last call in a [Call-Context Query](${doc_files_1.FlowrWikiBaseRef}/Query-API))
31
+ but does not incorporate it into its core analysis.
32
+
33
+
34
+ ${(0, doc_structure_1.block)({
35
+ type: 'TIP',
36
+ content: `If you want to investigate the Control Flow Graph,
37
+ you can use the ${(0, doc_cli_option_1.getReplCommand)('controlflow*')} command in the REPL (see the [Interface wiki page](${doc_files_1.FlowrWikiBaseRef}/Interface) for more information).`
38
+ })}
39
+
40
+ The CFG may be a little bit uncommon compared to the classical CFG with basic blocks. This is mostly due to historical reasons.
41
+ Please [open a new issue](${doc_issue_1.NewIssueUrl}) if you are interested in such a perspective.
42
+
43
+ But for now, let's look at a simple CFG for a program without any branching:
44
+
45
+ ${(0, doc_code_1.codeBlock)('r', 'x <- 2 * 3 + 1')}
46
+
47
+ The corresponding CFG is a directed, labeled graph with two types of edges (control and flow dependencies):
48
+
49
+ ${await (0, doc_cfg_1.printCFGCode)(shell, 'x <- 2 * 3 + 1', { showCode: false, prefix: 'flowchart RL\n' })}
50
+
51
+ Every normalized node of the [normalized AST](${doc_files_1.FlowrWikiBaseRef}/Normalized-AST) that has any relevance to the
52
+ execution is added and automatically linked using its id (similarly to vertices of the [dataflow graph](${doc_files_1.FlowrWikiBaseRef}/Dataflow%20Graph)).
53
+ Higher expressions, such as \`2 * 3\` get an additional node with an artificial id that ends in \`-exit\` to mark whenever their calculation is over.
54
+
55
+ To gain a better understanding, let's have a look at a simple program with a single branching structure:
56
+
57
+ ${await (0, doc_cfg_1.printCFGCode)(shell, 'if(u) 3 else 2', { showCode: true, openCode: true, prefix: 'flowchart RL\n' })}
58
+
59
+ Here, you can see the \`if\` node followed by the condition (in this case merely \`u\`) that then splits into two branches for the two possible outcomes.
60
+ The \`if\` structure is terminated by the corresponding \`-exit\` node.
61
+
62
+ For you to compare, the following shows the CFG of an \`if\` without an \`else\` branch:
63
+
64
+ ${await (0, doc_cfg_1.printCFGCode)(shell, 'if(u || v) 3', { showCode: true, openCode: false, prefix: 'flowchart RL\n' })}
65
+
66
+ The control flow graph also harmonizes with function definitions, and calls:
67
+
68
+ ${await (0, doc_cfg_1.printCFGCode)(shell, 'f <- function() { 3 }\nf()', { showCode: true, openCode: true, prefix: 'flowchart RL\n' })}
69
+
70
+ In general, it is probably best to use the ${(0, doc_cli_option_1.getReplCommand)('controlflow*')} command in the REPL to investigate the CFG interactively.
71
+ Have a look at the ${(0, doc_types_1.shortLink)(visitor_1.visitCfgInReverseOrder.name, types.info)} function for a generic CFG visitor.
72
+
73
+ `;
74
+ }
75
+ if (require.main === module) {
76
+ (0, log_1.setMinLevelOfAllLogs)(6 /* LogLevel.Fatal */);
77
+ const shell = new shell_1.RShell();
78
+ void getText(shell).then(str => {
79
+ console.log(str);
80
+ }).finally(() => {
81
+ shell.close();
82
+ });
83
+ }
84
+ //# sourceMappingURL=print-cfg-wiki.js.map