@eagleoutice/flowr 2.2.10 → 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 (132) 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/cli/repl/server/compact.d.ts +2 -2
  17. package/cli/repl/server/compact.js +3 -3
  18. package/cli/repl/server/messages/message-analysis.d.ts +2 -2
  19. package/cli/repl/server/messages/message-analysis.js +2 -2
  20. package/config.d.ts +27 -2
  21. package/config.js +30 -4
  22. package/dataflow/environments/built-in-config.d.ts +5 -2
  23. package/dataflow/environments/built-in-config.js +8 -2
  24. package/dataflow/environments/built-in.d.ts +8 -1
  25. package/dataflow/environments/built-in.js +8 -1
  26. package/dataflow/environments/clone.d.ts +5 -0
  27. package/dataflow/environments/clone.js +5 -0
  28. package/dataflow/environments/default-builtin-config.js +96 -10
  29. package/dataflow/environments/define.d.ts +5 -1
  30. package/dataflow/environments/define.js +36 -10
  31. package/dataflow/environments/environment.js +4 -2
  32. package/dataflow/environments/overwrite.js +4 -0
  33. package/dataflow/environments/remove.d.ts +6 -0
  34. package/dataflow/environments/remove.js +24 -0
  35. package/dataflow/environments/resolve-by-name.js +1 -1
  36. package/dataflow/extractor.d.ts +1 -1
  37. package/dataflow/extractor.js +8 -6
  38. package/dataflow/graph/dataflowgraph-builder.d.ts +76 -6
  39. package/dataflow/graph/dataflowgraph-builder.js +102 -6
  40. package/dataflow/graph/edge.js +4 -1
  41. package/dataflow/graph/graph.d.ts +12 -1
  42. package/dataflow/graph/graph.js +37 -0
  43. package/dataflow/graph/vertex.d.ts +42 -2
  44. package/dataflow/graph/vertex.js +32 -0
  45. package/dataflow/internal/linker.js +3 -1
  46. package/dataflow/internal/process/functions/call/argument/unpack-argument.d.ts +3 -0
  47. package/dataflow/internal/process/functions/call/argument/unpack-argument.js +4 -10
  48. package/dataflow/internal/process/functions/call/built-in/built-in-access.d.ts +1 -0
  49. package/dataflow/internal/process/functions/call/built-in/built-in-access.js +55 -45
  50. package/dataflow/internal/process/functions/call/built-in/built-in-apply.d.ts +6 -4
  51. package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +27 -8
  52. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +37 -7
  53. package/dataflow/internal/process/functions/call/built-in/built-in-eval.d.ts +10 -0
  54. package/dataflow/internal/process/functions/call/built-in/built-in-eval.js +140 -0
  55. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +4 -3
  56. package/dataflow/internal/process/functions/call/built-in/built-in-list.js +51 -17
  57. package/dataflow/internal/process/functions/call/built-in/built-in-pipe.js +21 -3
  58. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.d.ts +3 -0
  59. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +83 -29
  60. package/dataflow/internal/process/functions/call/built-in/built-in-rm.d.ts +7 -0
  61. package/dataflow/internal/process/functions/call/built-in/built-in-rm.js +41 -0
  62. package/dataflow/internal/process/functions/call/built-in/built-in-source.js +20 -6
  63. package/dataflow/internal/process/functions/call/built-in/built-in-vector.d.ts +15 -0
  64. package/dataflow/internal/process/functions/call/built-in/built-in-vector.js +75 -0
  65. package/dataflow/internal/process/functions/call/common.d.ts +1 -1
  66. package/dataflow/internal/process/functions/call/common.js +4 -2
  67. package/dataflow/internal/process/functions/call/named-call-handling.d.ts +2 -0
  68. package/dataflow/internal/process/functions/call/named-call-handling.js +9 -5
  69. package/dataflow/internal/process/process-named-call.d.ts +3 -0
  70. package/dataflow/internal/process/process-named-call.js +3 -0
  71. package/dataflow/processor.d.ts +7 -7
  72. package/documentation/data/server/doc-data-server-messages.js +2 -2
  73. package/documentation/doc-util/doc-cfg.d.ts +11 -2
  74. package/documentation/doc-util/doc-cfg.js +35 -6
  75. package/documentation/doc-util/doc-code.js +10 -2
  76. package/documentation/print-capabilities-markdown.js +1 -1
  77. package/documentation/print-cfg-wiki.d.ts +1 -0
  78. package/documentation/print-cfg-wiki.js +84 -0
  79. package/documentation/print-core-wiki.js +2 -2
  80. package/documentation/print-interface-wiki.js +4 -0
  81. package/documentation/print-query-wiki.js +22 -3
  82. package/package.json +4 -3
  83. package/queries/catalog/call-context-query/call-context-query-executor.js +13 -0
  84. package/queries/catalog/call-context-query/call-context-query-format.d.ts +4 -0
  85. package/queries/catalog/call-context-query/call-context-query-format.js +1 -0
  86. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +1 -1
  87. package/queries/catalog/dependencies-query/dependencies-query-executor.js +13 -5
  88. package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +1 -25
  89. package/queries/catalog/dependencies-query/dependencies-query-format.js +2 -145
  90. package/queries/catalog/dependencies-query/function-info/function-info.d.ts +24 -0
  91. package/queries/catalog/dependencies-query/function-info/function-info.js +10 -0
  92. package/queries/catalog/dependencies-query/function-info/library-functions.d.ts +2 -0
  93. package/queries/catalog/dependencies-query/function-info/library-functions.js +18 -0
  94. package/queries/catalog/dependencies-query/function-info/read-functions.d.ts +2 -0
  95. package/queries/catalog/dependencies-query/function-info/read-functions.js +101 -0
  96. package/queries/catalog/dependencies-query/function-info/source-functions.d.ts +2 -0
  97. package/queries/catalog/dependencies-query/function-info/source-functions.js +11 -0
  98. package/queries/catalog/dependencies-query/function-info/write-functions.d.ts +2 -0
  99. package/queries/catalog/dependencies-query/function-info/write-functions.js +87 -0
  100. package/queries/catalog/location-map-query/location-map-query-executor.d.ts +1 -1
  101. package/queries/catalog/location-map-query/location-map-query-executor.js +38 -3
  102. package/queries/catalog/location-map-query/location-map-query-format.d.ts +10 -1
  103. package/queries/catalog/location-map-query/location-map-query-format.js +5 -1
  104. package/queries/catalog/project-query/project-query-executor.d.ts +3 -0
  105. package/queries/catalog/project-query/project-query-executor.js +17 -0
  106. package/queries/catalog/project-query/project-query-format.d.ts +67 -0
  107. package/queries/catalog/project-query/project-query-format.js +26 -0
  108. package/queries/query.d.ts +60 -1
  109. package/queries/query.js +3 -1
  110. package/r-bridge/data/data.d.ts +2 -2
  111. package/r-bridge/data/data.js +2 -2
  112. package/slicing/static/fingerprint.js +8 -1
  113. package/slicing/static/slice-call.d.ts +1 -1
  114. package/slicing/static/slice-call.js +5 -16
  115. package/slicing/static/slicer-types.d.ts +2 -0
  116. package/slicing/static/static-slicer.d.ts +4 -2
  117. package/slicing/static/static-slicer.js +24 -18
  118. package/slicing/static/visiting-queue.d.ts +7 -1
  119. package/slicing/static/visiting-queue.js +20 -6
  120. package/util/arrays.d.ts +23 -0
  121. package/util/arrays.js +41 -0
  122. package/util/cfg/visitor.d.ts +1 -1
  123. package/util/cfg/visitor.js +2 -2
  124. package/util/{list-access.d.ts → containers.d.ts} +24 -4
  125. package/util/{list-access.js → containers.js} +42 -12
  126. package/util/mermaid/ast.js +12 -1
  127. package/util/mermaid/cfg.js +2 -2
  128. package/util/parallel.d.ts +2 -1
  129. package/util/parallel.js +11 -2
  130. package/util/prefix.d.ts +13 -0
  131. package/util/prefix.js +34 -0
  132. package/util/version.js +1 -1
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processEvalCall = processEvalCall;
4
+ const info_1 = require("../../../../../info");
5
+ const config_1 = require("../../../../../../config");
6
+ const known_call_handling_1 = require("../known-call-handling");
7
+ const retriever_1 = require("../../../../../../r-bridge/retriever");
8
+ const decorate_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate");
9
+ const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
10
+ const logger_1 = require("../../../../../logger");
11
+ const log_1 = require("../../../../../../util/log");
12
+ const built_in_source_1 = require("./built-in-source");
13
+ const edge_1 = require("../../../../../graph/edge");
14
+ const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
15
+ const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
16
+ const append_1 = require("../../../../../environments/append");
17
+ const assert_1 = require("../../../../../../util/assert");
18
+ const arrays_1 = require("../../../../../../util/arrays");
19
+ function processEvalCall(name, args, rootId, data, config) {
20
+ if (args.length !== 1 || args[0] === r_function_call_1.EmptyArgument || !args[0].value) {
21
+ logger_1.dataflowLogger.warn(`Expected exactly one argument for eval currently, but got ${args.length} instead, skipping`);
22
+ return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
23
+ }
24
+ const information = config.includeFunctionCall ?
25
+ (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: [true] }).information
26
+ : (0, info_1.initializeCleanDataflowInformation)(rootId, data);
27
+ const evalArgument = args[0];
28
+ if (config.includeFunctionCall) {
29
+ information.graph.addEdge(rootId, args[0].value.info.id, edge_1.EdgeType.Returns);
30
+ }
31
+ if (!(0, config_1.getConfig)().solver.evalStrings) {
32
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Skipping eval call ${JSON.stringify(evalArgument)} (disabled in config file)`);
33
+ information.graph.markIdForUnknownSideEffects(rootId);
34
+ return information;
35
+ }
36
+ const code = resolveEvalToCode(evalArgument.value, data.environment, data.completeAst.idMap);
37
+ if (code) {
38
+ const idGenerator = (0, decorate_1.sourcedDeterministicCountingIdGenerator)(name.lexeme + '::' + rootId, name.location);
39
+ data = {
40
+ ...data,
41
+ controlDependencies: [...(data.controlDependencies ?? []), { id: rootId, when: true }]
42
+ };
43
+ const originalInfo = { ...information };
44
+ const result = [];
45
+ for (const c of code) {
46
+ const codeRequest = (0, retriever_1.requestFromInput)(c);
47
+ const r = (0, built_in_source_1.sourceRequest)(rootId, codeRequest, data, originalInfo, idGenerator);
48
+ result.push(r);
49
+ // add a returns edge from the eval to the result
50
+ for (const e of r.exitPoints) {
51
+ information.graph.addEdge(rootId, e, edge_1.EdgeType.Returns);
52
+ }
53
+ }
54
+ return {
55
+ graph: result.reduce((acc, r) => acc.mergeWith(r.graph), information.graph),
56
+ environment: result.reduce((acc, r) => (0, append_1.appendEnvironment)(acc, r.environment), information.environment),
57
+ entryPoint: rootId,
58
+ out: [...information.out, ...result.flatMap(r => r.out)],
59
+ in: [...information.in, ...result.flatMap(r => r.in)],
60
+ unknownReferences: [...information.unknownReferences, ...result.flatMap(r => r.unknownReferences)],
61
+ exitPoints: [...information.exitPoints, ...result.flatMap(r => r.exitPoints)],
62
+ };
63
+ }
64
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Non-constant argument ${JSON.stringify(args)} for eval is currently not supported, skipping`);
65
+ information.graph.markIdForUnknownSideEffects(rootId);
66
+ return information;
67
+ }
68
+ function resolveEvalToCode(evalArgument, env, idMap) {
69
+ const val = evalArgument;
70
+ if (val.type === type_1.RType.FunctionCall && val.named && val.functionName.content === 'parse') {
71
+ const arg = val.arguments.find(v => v !== r_function_call_1.EmptyArgument && v.name?.content === 'text');
72
+ const nArg = val.arguments.find(v => v !== r_function_call_1.EmptyArgument && v.name?.content === 'n');
73
+ if (nArg !== undefined || arg === undefined || arg === r_function_call_1.EmptyArgument) {
74
+ return undefined;
75
+ }
76
+ if (arg.value?.type === type_1.RType.String) {
77
+ return [arg.value.content.str];
78
+ }
79
+ else if (arg.value?.type === type_1.RType.Symbol) {
80
+ const resolve = (0, resolve_by_name_1.resolveValueOfVariable)(arg.value.content, env, idMap);
81
+ if (resolve && resolve.every(r => typeof r === 'object' && r !== null && 'str' in r)) {
82
+ return resolve.map(r => r.str);
83
+ }
84
+ }
85
+ else if (arg.value?.type === type_1.RType.FunctionCall && arg.value.named && ['paste', 'paste0'].includes(arg.value.functionName.content)) {
86
+ return handlePaste(arg.value.arguments, env, idMap, arg.value.functionName.content === 'paste' ? [' '] : ['']);
87
+ }
88
+ return undefined;
89
+ }
90
+ else if (val.type === type_1.RType.Symbol) {
91
+ // const resolved = resolveValueOfVariable(val.content, env);
92
+ // see https://github.com/flowr-analysis/flowr/pull/1467
93
+ return undefined;
94
+ }
95
+ else {
96
+ return undefined;
97
+ }
98
+ }
99
+ function getAsString(val, env, idMap) {
100
+ if (!val) {
101
+ return undefined;
102
+ }
103
+ if (val.type === type_1.RType.String) {
104
+ return [val.content.str];
105
+ }
106
+ else if (val.type === type_1.RType.Symbol) {
107
+ const resolved = (0, resolve_by_name_1.resolveValueOfVariable)(val.content, env, idMap);
108
+ if (resolved && resolved.every(r => typeof r === 'object' && r !== null && 'str' in r)) {
109
+ return resolved.map(r => r.str);
110
+ }
111
+ }
112
+ return undefined;
113
+ }
114
+ function handlePaste(args, env, idMap, sepDefault) {
115
+ const sepArg = args.find(v => v !== r_function_call_1.EmptyArgument && v.name?.content === 'sep');
116
+ if (sepArg) {
117
+ const res = sepArg !== r_function_call_1.EmptyArgument && sepArg.value ? getAsString(sepArg.value, env, idMap) : undefined;
118
+ if (!res) {
119
+ // sep not resolvable clearly / unknown
120
+ return undefined;
121
+ }
122
+ sepDefault = res;
123
+ }
124
+ const allArgs = args
125
+ .filter(v => v !== r_function_call_1.EmptyArgument && v.name?.content !== 'sep' && v.value)
126
+ .map(v => getAsString(v.value, env, idMap));
127
+ if (allArgs.some(assert_1.isUndefined)) {
128
+ return undefined;
129
+ }
130
+ // return all cartesian products using the separator
131
+ const result = [];
132
+ const cartesianProducts = (0, arrays_1.cartesianProduct)(...allArgs);
133
+ for (const sep of sepDefault) {
134
+ for (const c of cartesianProducts) {
135
+ result.push(c.join(sep));
136
+ }
137
+ }
138
+ return result;
139
+ }
140
+ //# sourceMappingURL=built-in-eval.js.map
@@ -17,6 +17,7 @@ const scoping_1 = require("../../../../../environments/scoping");
17
17
  const built_in_1 = require("../../../../../environments/built-in");
18
18
  const overwrite_1 = require("../../../../../environments/overwrite");
19
19
  const logger_1 = require("../../../../../logger");
20
+ const log_1 = require("../../../../../../util/log");
20
21
  const dotDotDotAccess = /^\.\.\d+$/;
21
22
  function linkReadNameToWriteIfPossible(read, environments, listEnvironments, remainingRead, nextGraph) {
22
23
  const readName = read.name && dotDotDotAccess.test(read.name) ? '...' : read.name;
@@ -83,7 +84,7 @@ function updateSideEffectsForCalledFunctions(calledEnvs, inputEnvironment, nextG
83
84
  }
84
85
  function processExpressionList(name, args, rootId, data) {
85
86
  const expressions = args.map(e => (0, unpack_argument_1.unpackArgument)(e));
86
- logger_1.dataflowLogger.trace(() => `[expr list] with ${expressions.length} expressions`);
87
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `[expr list] with ${expressions.length} expressions`);
87
88
  let { environment } = data;
88
89
  // used to detect if a "write" happens within the same expression list
89
90
  const listEnvironments = new Set();
@@ -95,7 +96,7 @@ function processExpressionList(name, args, rootId, data) {
95
96
  const processedExpressions = [];
96
97
  let defaultReturnExpr = undefined;
97
98
  for (const expression of expressions) {
98
- logger_1.dataflowLogger.trace(`processing expression ${++expressionCounter} of ${expressions.length}`);
99
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `processing expression ${++expressionCounter} of ${expressions.length}`);
99
100
  if (expression === undefined) {
100
101
  processedExpressions.push(undefined);
101
102
  continue;
@@ -115,7 +116,7 @@ function processExpressionList(name, args, rootId, data) {
115
116
  }
116
117
  (0, info_1.addNonDefaultExitPoints)(exitPoints, processed.exitPoints);
117
118
  out.push(...processed.out);
118
- logger_1.dataflowLogger.trace(`expression ${expressionCounter} of ${expressions.length} has ${processed.unknownReferences.length} unknown nodes`);
119
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `expression ${expressionCounter} of ${expressions.length} has ${processed.unknownReferences.length} unknown nodes`);
119
120
  processNextExpression(processed, environment, listEnvironments, remainingRead, nextGraph);
120
121
  environment = exitPoints.length > 0 ? (0, overwrite_1.overwriteEnvironment)(environment, processed.environment) : processed.environment;
121
122
  const calledEnvs = (0, linker_1.linkFunctionCalls)(nextGraph, data.completeAst.idMap, processed.graph);
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.processList = processList;
4
4
  const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
5
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
6
  const known_call_handling_1 = require("../known-call-handling");
8
7
  const config_1 = require("../../../../../../config");
8
+ const containers_1 = require("../../../../../../util/containers");
9
9
  /**
10
10
  * Process a list call.
11
11
  *
@@ -15,36 +15,70 @@ const config_1 = require("../../../../../../config");
15
15
  * ```
16
16
  */
17
17
  function processList(name, args, rootId, data) {
18
+ const fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data });
18
19
  if (!(0, config_1.getConfig)().solver.pointerTracking) {
19
- return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
20
+ return fnCall.information;
20
21
  }
21
- const namedArguments = [];
22
+ const listArgs = [];
22
23
  for (const arg of args) {
23
24
  // Skip non named arguments
24
- if (arg === r_function_call_1.EmptyArgument || arg.type !== type_1.RType.Argument || arg.name === undefined) {
25
+ if (arg === r_function_call_1.EmptyArgument || arg.type !== type_1.RType.Argument || arg.value === undefined) {
25
26
  continue;
26
27
  }
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) {
28
+ let newIndex;
29
+ if (arg.name) {
30
+ // Named argument
31
+ newIndex = {
32
+ identifier: {
33
+ index: arg.info.index,
34
+ lexeme: arg.name.content
35
+ },
36
+ nodeId: arg.info.id,
37
+ };
38
+ }
39
+ else {
40
+ // Unnamed argument
41
+ newIndex = {
42
+ identifier: {
43
+ index: arg.info.index,
44
+ },
45
+ nodeId: arg.value.info.id,
46
+ };
47
+ }
48
+ // Check whether argument value can be resolved
49
+ if (arg.value.type === type_1.RType.Symbol) {
50
+ const indicesCollection = (0, containers_1.resolveIndicesByName)(arg.value.lexeme, data.environment);
51
+ if (indicesCollection) {
36
52
  newIndex = {
37
53
  ...newIndex,
38
- subIndices: indices,
54
+ subIndices: indicesCollection,
39
55
  };
40
56
  }
41
57
  }
42
- namedArguments.push(newIndex);
58
+ else {
59
+ // Check whether argument is nested container
60
+ const indicesCollection = fnCall.information.graph.getVertex(arg.value.info.id)?.indicesCollection;
61
+ if (indicesCollection) {
62
+ newIndex = {
63
+ ...newIndex,
64
+ subIndices: indicesCollection,
65
+ };
66
+ }
67
+ }
68
+ listArgs.push(newIndex);
69
+ }
70
+ if ((0, config_1.isOverPointerAnalysisThreshold)(listArgs.length)) {
71
+ return fnCall.information;
43
72
  }
44
73
  const indices = {
45
- indices: namedArguments,
74
+ indices: listArgs,
46
75
  isContainer: true,
47
76
  };
48
- return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }, [indices]).information;
77
+ // Add resolved indices to vertex
78
+ const vertex = fnCall.information.graph.getVertex(rootId);
79
+ if (vertex) {
80
+ vertex.indicesCollection = [indices];
81
+ }
82
+ return fnCall.information;
49
83
  }
50
84
  //# sourceMappingURL=built-in-list.js.map
@@ -35,11 +35,29 @@ function processPipe(name, args, rootId, data) {
35
35
  information.graph.addEdge(functionCallNode.id, argId, edge_1.EdgeType.Argument | edge_1.EdgeType.Reads);
36
36
  }
37
37
  const firstArgument = processedArguments[0];
38
+ const uniqueIn = [...information.in];
39
+ for (const ing of (firstArgument?.in ?? [])) {
40
+ if (!uniqueIn.find(e => e.nodeId === ing.nodeId)) {
41
+ uniqueIn.push(ing);
42
+ }
43
+ }
44
+ const uniqueOut = [...information.out];
45
+ for (const outg of (firstArgument?.out ?? [])) {
46
+ if (!uniqueOut.find(e => e.nodeId === outg.nodeId)) {
47
+ uniqueOut.push(outg);
48
+ }
49
+ }
50
+ const uniqueUnknownReferences = [...information.unknownReferences];
51
+ for (const unknown of (firstArgument?.unknownReferences ?? [])) {
52
+ if (!uniqueUnknownReferences.find(e => e.nodeId === unknown.nodeId)) {
53
+ uniqueUnknownReferences.push(unknown);
54
+ }
55
+ }
38
56
  return {
39
57
  ...information,
40
- in: [...information.in, ...firstArgument.in],
41
- out: [...information.out, ...firstArgument.out],
42
- unknownReferences: [...information.unknownReferences, ...firstArgument.unknownReferences],
58
+ in: uniqueIn,
59
+ out: uniqueOut,
60
+ unknownReferences: uniqueUnknownReferences,
43
61
  entryPoint: rootId
44
62
  };
45
63
  }
@@ -5,9 +5,12 @@ import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/
5
5
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
6
  import { type RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
7
7
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
8
+ import type { ContainerIndicesCollection } from '../../../../../graph/vertex';
8
9
  export declare function processReplacementFunction<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>,
9
10
  /** The last one has to be the value */
10
11
  args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: {
11
12
  makeMaybe?: boolean;
12
13
  assignmentOperator?: '<-' | '<<-';
14
+ readIndices?: boolean;
15
+ activeIndices?: ContainerIndicesCollection;
13
16
  } & ForceArguments): DataflowInformation;
@@ -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
- (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `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`);
@@ -172,7 +178,8 @@ function sourceRequest(rootId, request, data, information, getId) {
172
178
  });
173
179
  }
174
180
  catch (e) {
175
- logger_1.dataflowLogger.warn(`Failed to analyze sourced file ${JSON.stringify(request)}, skipping: ${e.message}`);
181
+ logger_1.dataflowLogger.error(`Failed to analyze sourced file ${JSON.stringify(request)}, skipping: ${e.message}`);
182
+ logger_1.dataflowLogger.error(e.stack);
176
183
  information.graph.markIdForUnknownSideEffects(rootId);
177
184
  return information;
178
185
  }
@@ -183,6 +190,7 @@ function sourceRequest(rootId, request, data, information, getId) {
183
190
  for (const out of dataflow.out) {
184
191
  dataflow.graph.addControlDependency(out.nodeId, rootId, true);
185
192
  }
193
+ dataflow.graph.addFile(request.request === 'file' ? request.content : '<inline>');
186
194
  // update our graph with the sourced file's information
187
195
  const newInformation = { ...information };
188
196
  newInformation.environment = (0, overwrite_1.overwriteEnvironment)(information.environment, dataflow.environment);
@@ -191,7 +199,13 @@ function sourceRequest(rootId, request, data, information, getId) {
191
199
  for (const [k, v] of normalized.idMap) {
192
200
  data.completeAst.idMap.set(k, v);
193
201
  }
194
- 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
+ };
195
209
  }
196
210
  function standaloneSourceFile(inputRequest, data, uniqueSourceId, information) {
197
211
  const path = inputRequest.request === 'file' ? inputRequest.content : '-inline-';
@@ -208,6 +222,6 @@ function standaloneSourceFile(inputRequest, data, uniqueSourceId, information) {
208
222
  currentRequest: request,
209
223
  environment: information.environment,
210
224
  referenceChain: [...data.referenceChain, inputRequest]
211
- }, information, (0, decorate_1.deterministicPrefixIdGenerator)(path + '@' + uniqueSourceId));
225
+ }, information, (0, decorate_1.deterministicPrefixIdGenerator)(path + '::' + uniqueSourceId));
212
226
  }
213
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;