@eagleoutice/flowr 2.7.6 → 2.8.1

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 (208) hide show
  1. package/README.md +67 -64
  2. package/cli/wiki.js +1 -1
  3. package/control-flow/extract-cfg.js +3 -3
  4. package/control-flow/useless-loop.d.ts +1 -1
  5. package/control-flow/useless-loop.js +2 -2
  6. package/dataflow/cluster.js +3 -3
  7. package/dataflow/environments/built-in-config.d.ts +8 -4
  8. package/dataflow/environments/built-in.d.ts +27 -14
  9. package/dataflow/environments/built-in.js +27 -12
  10. package/dataflow/environments/default-builtin-config.d.ts +615 -3
  11. package/dataflow/environments/default-builtin-config.js +50 -15
  12. package/dataflow/environments/environment.js +3 -2
  13. package/dataflow/environments/identifier.d.ts +5 -1
  14. package/dataflow/environments/reference-to-maybe.d.ts +2 -2
  15. package/dataflow/environments/reference-to-maybe.js +23 -14
  16. package/dataflow/environments/resolve-by-name.d.ts +6 -2
  17. package/dataflow/environments/resolve-by-name.js +5 -1
  18. package/dataflow/environments/scoping.js +1 -3
  19. package/dataflow/eval/resolve/alias-tracking.js +5 -1
  20. package/dataflow/extractor.js +3 -3
  21. package/dataflow/fn/exceptions-of-function.d.ts +13 -0
  22. package/dataflow/fn/exceptions-of-function.js +47 -0
  23. package/dataflow/fn/higher-order-function.d.ts +1 -1
  24. package/dataflow/fn/higher-order-function.js +3 -3
  25. package/dataflow/fn/recursive-function.d.ts +6 -0
  26. package/dataflow/fn/recursive-function.js +32 -0
  27. package/dataflow/graph/call-graph.d.ts +10 -0
  28. package/dataflow/graph/call-graph.js +212 -0
  29. package/dataflow/graph/dataflowgraph-builder.d.ts +7 -2
  30. package/dataflow/graph/dataflowgraph-builder.js +14 -9
  31. package/dataflow/graph/diff-dataflow-graph.js +96 -2
  32. package/dataflow/graph/graph.d.ts +10 -7
  33. package/dataflow/graph/graph.js +7 -8
  34. package/dataflow/graph/vertex.d.ts +6 -3
  35. package/dataflow/hooks.d.ts +30 -0
  36. package/dataflow/hooks.js +38 -0
  37. package/dataflow/info.d.ts +28 -5
  38. package/dataflow/info.js +66 -31
  39. package/dataflow/internal/linker.d.ts +13 -3
  40. package/dataflow/internal/linker.js +163 -73
  41. package/dataflow/internal/process/functions/call/argument/unpack-argument.d.ts +4 -0
  42. package/dataflow/internal/process/functions/call/argument/unpack-argument.js +7 -0
  43. package/dataflow/internal/process/functions/call/built-in/built-in-apply.d.ts +1 -1
  44. package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +19 -3
  45. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +14 -0
  46. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +30 -0
  47. package/dataflow/internal/process/functions/call/built-in/built-in-eval.js +2 -1
  48. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +24 -17
  49. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +2 -1
  50. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.d.ts +5 -1
  51. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +59 -21
  52. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +4 -3
  53. package/dataflow/internal/process/functions/call/built-in/built-in-register-hook.d.ts +34 -0
  54. package/dataflow/internal/process/functions/call/built-in/built-in-register-hook.js +92 -0
  55. package/dataflow/internal/process/functions/call/built-in/built-in-repeat-loop.js +1 -0
  56. package/dataflow/internal/process/functions/call/built-in/built-in-stop-if-not.d.ts +21 -0
  57. package/dataflow/internal/process/functions/call/built-in/built-in-stop-if-not.js +129 -0
  58. package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.d.ts +16 -0
  59. package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.js +127 -0
  60. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +5 -3
  61. package/dataflow/internal/process/functions/call/common.d.ts +13 -1
  62. package/dataflow/internal/process/functions/call/common.js +33 -2
  63. package/dataflow/internal/process/functions/call/known-call-handling.d.ts +13 -1
  64. package/dataflow/internal/process/functions/call/known-call-handling.js +29 -3
  65. package/dataflow/internal/process/functions/call/named-call-handling.js +2 -1
  66. package/dataflow/internal/process/functions/call/unnamed-call-handling.js +6 -4
  67. package/dataflow/internal/process/functions/process-argument.js +7 -6
  68. package/dataflow/internal/process/functions/process-parameter.js +2 -1
  69. package/dataflow/internal/process/process-named-call.d.ts +2 -2
  70. package/dataflow/internal/process/process-symbol.js +3 -2
  71. package/dataflow/internal/process/process-value.d.ts +3 -2
  72. package/dataflow/internal/process/process-value.js +8 -6
  73. package/dataflow/origin/dfg-get-origin.js +2 -1
  74. package/dataflow/origin/dfg-get-symbol-refs.js +1 -1
  75. package/documentation/doc-readme.d.ts +1 -1
  76. package/documentation/doc-readme.js +6 -6
  77. package/documentation/doc-util/doc-code.js +1 -1
  78. package/documentation/doc-util/doc-dfg.d.ts +1 -0
  79. package/documentation/doc-util/doc-dfg.js +7 -4
  80. package/documentation/doc-util/doc-query.d.ts +1 -0
  81. package/documentation/doc-util/doc-query.js +1 -1
  82. package/documentation/doc-util/doc-repl.d.ts +2 -1
  83. package/documentation/doc-util/doc-repl.js +11 -3
  84. package/documentation/wiki-analyzer.js +2 -0
  85. package/documentation/wiki-dataflow-graph.js +59 -16
  86. package/documentation/wiki-interface.js +33 -5
  87. package/documentation/wiki-mk/doc-context.d.ts +2 -1
  88. package/documentation/wiki-mk/doc-context.js +2 -2
  89. package/documentation/wiki-mk/doc-maker.js +4 -3
  90. package/documentation/wiki-normalized-ast.js +6 -0
  91. package/documentation/wiki-query.js +109 -1
  92. package/linter/linter-rules.d.ts +1 -1
  93. package/linter/rules/seeded-randomness.js +17 -12
  94. package/linter/rules/useless-loop.d.ts +1 -1
  95. package/package.json +9 -9
  96. package/project/cache/flowr-analyzer-cache.d.ts +11 -0
  97. package/project/cache/flowr-analyzer-cache.js +19 -0
  98. package/project/context/flowr-analyzer-dependencies-context.d.ts +6 -1
  99. package/project/context/flowr-analyzer-dependencies-context.js +6 -0
  100. package/project/context/flowr-analyzer-files-context.d.ts +5 -2
  101. package/project/context/flowr-analyzer-files-context.js +24 -17
  102. package/project/context/flowr-file.d.ts +9 -4
  103. package/project/context/flowr-file.js +20 -6
  104. package/project/flowr-analyzer.d.ts +11 -0
  105. package/project/flowr-analyzer.js +6 -0
  106. package/project/plugins/file-plugins/files/flowr-description-file.d.ts +8 -0
  107. package/project/plugins/file-plugins/files/flowr-description-file.js +36 -3
  108. package/project/plugins/file-plugins/files/flowr-jupyter-file.js +1 -1
  109. package/project/plugins/file-plugins/files/flowr-namespace-file.js +1 -1
  110. package/project/plugins/file-plugins/files/flowr-news-file.js +1 -1
  111. package/project/plugins/file-plugins/files/flowr-rmarkdown-file.js +1 -1
  112. package/project/plugins/file-plugins/flowr-analyzer-description-file-plugin.js +1 -1
  113. package/project/plugins/file-plugins/flowr-analyzer-file-plugin.d.ts +4 -1
  114. package/project/plugins/file-plugins/flowr-analyzer-file-plugin.js +3 -0
  115. package/project/plugins/file-plugins/{flowr-analyzer-namespace-file-plugin.d.ts → flowr-analyzer-namespace-files-plugin.d.ts} +1 -1
  116. package/project/plugins/file-plugins/{flowr-analyzer-namespace-file-plugin.js → flowr-analyzer-namespace-files-plugin.js} +4 -4
  117. package/project/plugins/file-plugins/flowr-analyzer-test-file-plugin.d.ts +26 -0
  118. package/project/plugins/file-plugins/flowr-analyzer-test-file-plugin.js +39 -0
  119. package/project/plugins/file-plugins/flowr-analyzer-vignette-file-plugin.d.ts +26 -0
  120. package/project/plugins/file-plugins/flowr-analyzer-vignette-file-plugin.js +39 -0
  121. package/project/plugins/flowr-analyzer-plugin-defaults.js +6 -2
  122. package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-description-file-plugin.js +3 -13
  123. package/project/plugins/package-version-plugins/package.d.ts +1 -1
  124. package/project/plugins/package-version-plugins/package.js +3 -3
  125. package/project/plugins/plugin-registry.d.ts +4 -2
  126. package/project/plugins/plugin-registry.js +6 -2
  127. package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.d.ts +11 -0
  128. package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +5 -2
  129. package/queries/catalog/call-context-query/call-context-query-format.d.ts +4 -12
  130. package/queries/catalog/call-graph-query/call-graph-query-executor.d.ts +6 -0
  131. package/queries/catalog/call-graph-query/call-graph-query-executor.js +21 -0
  132. package/queries/catalog/call-graph-query/call-graph-query-format.d.ts +21 -0
  133. package/queries/catalog/call-graph-query/call-graph-query-format.js +32 -0
  134. package/queries/catalog/dataflow-query/dataflow-query-executor.js +4 -3
  135. package/queries/catalog/dependencies-query/dependencies-query-executor.js +29 -3
  136. package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +1 -0
  137. package/queries/catalog/dependencies-query/function-info/function-info.d.ts +8 -1
  138. package/queries/catalog/dependencies-query/function-info/write-functions.js +13 -0
  139. package/queries/catalog/does-call-query/does-call-query-executor.d.ts +6 -0
  140. package/queries/catalog/does-call-query/does-call-query-executor.js +100 -0
  141. package/queries/catalog/does-call-query/does-call-query-format.d.ts +51 -0
  142. package/queries/catalog/does-call-query/does-call-query-format.js +102 -0
  143. package/queries/catalog/files-query/files-query-executor.js +4 -4
  144. package/queries/catalog/files-query/files-query-format.d.ts +2 -1
  145. package/queries/catalog/files-query/files-query-format.js +18 -2
  146. package/queries/catalog/id-map-query/id-map-query-executor.js +4 -3
  147. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-executor.d.ts +18 -0
  148. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-executor.js +56 -0
  149. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-format.d.ts +34 -0
  150. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-format.js +54 -0
  151. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.js +3 -3
  152. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.d.ts +6 -0
  153. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.js +12 -0
  154. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-executor.d.ts +6 -0
  155. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-executor.js +23 -0
  156. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-format.d.ts +28 -0
  157. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-format.js +44 -0
  158. package/queries/catalog/linter-query/linter-query-format.js +4 -1
  159. package/queries/catalog/location-map-query/location-map-query-executor.js +1 -1
  160. package/queries/catalog/normalized-ast-query/normalized-ast-query-executor.js +4 -3
  161. package/queries/catalog/project-query/project-query-executor.js +9 -3
  162. package/queries/catalog/project-query/project-query-format.d.ts +6 -1
  163. package/queries/catalog/project-query/project-query-format.js +35 -9
  164. package/queries/query.d.ts +34 -2
  165. package/queries/query.js +9 -0
  166. package/r-bridge/data/data.d.ts +10 -5
  167. package/r-bridge/data/data.js +11 -5
  168. package/r-bridge/lang-4.x/ast/model/model.d.ts +7 -7
  169. package/r-bridge/lang-4.x/ast/model/nodes/r-access.d.ts +2 -2
  170. package/r-bridge/lang-4.x/ast/model/nodes/r-argument.d.ts +2 -2
  171. package/r-bridge/lang-4.x/ast/model/nodes/r-binary-op.d.ts +2 -2
  172. package/r-bridge/lang-4.x/ast/model/nodes/r-comment.d.ts +5 -2
  173. package/r-bridge/lang-4.x/ast/model/nodes/r-comment.js +8 -0
  174. package/r-bridge/lang-4.x/ast/model/nodes/r-expression-list.d.ts +2 -2
  175. package/r-bridge/lang-4.x/ast/model/nodes/r-for-loop.d.ts +2 -2
  176. package/r-bridge/lang-4.x/ast/model/nodes/r-function-call.d.ts +3 -3
  177. package/r-bridge/lang-4.x/ast/model/nodes/r-function-definition.d.ts +2 -2
  178. package/r-bridge/lang-4.x/ast/model/nodes/r-if-then-else.d.ts +2 -2
  179. package/r-bridge/lang-4.x/ast/model/nodes/r-parameter.d.ts +2 -2
  180. package/r-bridge/lang-4.x/ast/model/nodes/r-pipe.d.ts +2 -2
  181. package/r-bridge/lang-4.x/ast/model/nodes/r-repeat-loop.d.ts +2 -2
  182. package/r-bridge/lang-4.x/ast/model/nodes/r-unary-op.d.ts +2 -2
  183. package/r-bridge/lang-4.x/ast/model/nodes/r-while-loop.d.ts +2 -2
  184. package/r-bridge/lang-4.x/ast/parser/main/internal/other/normalize-comment.js +0 -1
  185. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +0 -2
  186. package/r-bridge/roxygen2/roxygen-ast.d.ts +218 -0
  187. package/r-bridge/roxygen2/roxygen-ast.js +82 -0
  188. package/r-bridge/roxygen2/roxygen-parse.d.ts +24 -0
  189. package/r-bridge/roxygen2/roxygen-parse.js +214 -0
  190. package/reconstruct/auto-select/magic-comments.js +4 -4
  191. package/slicing/static/slice-call.js +3 -4
  192. package/slicing/static/static-slicer.js +2 -2
  193. package/statistics/features/supported/defined-functions/defined-functions.js +1 -1
  194. package/util/collections/defaultmap.d.ts +3 -3
  195. package/util/mermaid/dfg.js +5 -5
  196. package/util/objects.js +1 -1
  197. package/util/r-author.d.ts +5 -0
  198. package/util/r-author.js +110 -0
  199. package/util/r-license.d.ts +10 -1
  200. package/util/r-license.js +27 -6
  201. package/util/r-version.d.ts +19 -0
  202. package/util/r-version.js +106 -0
  203. package/util/range.d.ts +6 -0
  204. package/util/range.js +7 -0
  205. package/util/simple-df/dfg-ascii.js +2 -2
  206. package/util/text/args.d.ts +9 -0
  207. package/util/text/args.js +65 -0
  208. package/util/version.js +1 -1
@@ -0,0 +1,32 @@
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
+ exports.CallGraphQueryDefinition = void 0;
7
+ const call_graph_query_executor_1 = require("./call-graph-query-executor");
8
+ const ansi_1 = require("../../../util/text/ansi");
9
+ const time_1 = require("../../../util/text/time");
10
+ const dfg_1 = require("../../../util/mermaid/dfg");
11
+ const joi_1 = __importDefault(require("joi"));
12
+ exports.CallGraphQueryDefinition = {
13
+ executor: call_graph_query_executor_1.executeCallGraphQuery,
14
+ asciiSummarizer: (formatter, _analyzer, queryResults, result) => {
15
+ const out = queryResults;
16
+ result.push(`Query: ${(0, ansi_1.bold)('call-graph', formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
17
+ result.push(` ╰ [Call Graph](${(0, dfg_1.graphToMermaidUrl)(out.graph)})`);
18
+ return true;
19
+ },
20
+ schema: joi_1.default.object({
21
+ type: joi_1.default.string().valid('call-graph').required().description('The type of the query.'),
22
+ }).description('A query to compute the Call Graph of the analyzed project.'),
23
+ flattenInvolvedNodes: queryResults => {
24
+ const flattened = [];
25
+ const out = queryResults;
26
+ for (const id of out.graph.idMap?.keys() ?? []) {
27
+ flattened.push(id);
28
+ }
29
+ return flattened;
30
+ }
31
+ };
32
+ //# sourceMappingURL=call-graph-query-format.js.map
@@ -9,12 +9,13 @@ async function executeDataflowQuery({ analyzer }, queries) {
9
9
  if (queries.length !== 1) {
10
10
  log_1.log.warn('Dataflow query expects only up to one query, but got', queries.length);
11
11
  }
12
+ const startTime = Date.now();
13
+ const graph = (await analyzer.dataflow()).graph;
12
14
  return {
13
15
  '.meta': {
14
- /* there is no sense in measuring a get */
15
- timing: 0
16
+ timing: Date.now() - startTime
16
17
  },
17
- graph: (await analyzer.dataflow()).graph
18
+ graph
18
19
  };
19
20
  }
20
21
  //# sourceMappingURL=dataflow-query-executor.js.map
@@ -79,17 +79,40 @@ function getResults(queries, { dataflow, config, normalize }, results, kind, fun
79
79
  const args = (0, resolve_argument_1.getArgumentStringValue)(config.solver.variables, dataflow.graph, vertex, info.argIdx, info.argName, info.resolveValue, data.analyzer.inspectContext());
80
80
  const linkedArgs = collectValuesFromLinks(args, { dataflow, config, ctx: data.analyzer.inspectContext() }, linkedIds);
81
81
  const linked = dropInfoOnLinkedIds(linkedIds);
82
+ function ignoreOnArgVal() {
83
+ if (info.ignoreIf === 'arg-true' || info.ignoreIf === 'arg-false') {
84
+ const margs = info.additionalArgs?.val;
85
+ (0, assert_1.guard)(margs, 'Need additional argument val when checking for arg-true');
86
+ const valArgs = (0, resolve_argument_1.getArgumentStringValue)(config.solver.variables, dataflow.graph, vertex, margs.argIdx, margs.argName, margs.resolveValue, data?.analyzer.inspectContext());
87
+ const valValues = valArgs?.values().flatMap(v => Array.from(v)).toArray() ?? [];
88
+ if (valValues.length === 0) {
89
+ return false;
90
+ }
91
+ if (info.ignoreIf === 'arg-true' && valValues.every(v => v === 'TRUE')) {
92
+ // all values are TRUE, so we can ignore this
93
+ return true;
94
+ }
95
+ else if (info.ignoreIf === 'arg-false' && valValues.every(v => v === 'FALSE')) {
96
+ // all values are FALSE, so we can ignore this
97
+ return true;
98
+ }
99
+ }
100
+ return false;
101
+ }
82
102
  const foundValues = linkedArgs ?? args;
83
103
  if (!foundValues) {
84
104
  if (info.ignoreIf === 'arg-missing') {
85
105
  return [];
86
106
  }
107
+ else if (ignoreOnArgVal()) {
108
+ return [];
109
+ }
87
110
  const record = (0, objects_1.compactRecord)({
88
111
  nodeId: id,
89
112
  functionName: vertex.name,
90
113
  lexemeOfArgument: undefined,
91
114
  linkedIds: linked?.length ? linked : undefined,
92
- value: defaultValue
115
+ value: info.defaultValue ?? defaultValue
93
116
  });
94
117
  return record ? [record] : [];
95
118
  }
@@ -98,7 +121,7 @@ function getResults(queries, { dataflow, config, normalize }, results, kind, fun
98
121
  const margs = info.additionalArgs?.mode;
99
122
  (0, assert_1.guard)(margs, 'Need additional argument mode when checking for mode');
100
123
  const modeArgs = (0, resolve_argument_1.getArgumentStringValue)(config.solver.variables, dataflow.graph, vertex, margs.argIdx, margs.argName, margs.resolveValue, data?.analyzer.inspectContext());
101
- const modeValues = modeArgs?.values().flatMap(v => [...v]) ?? [];
124
+ const modeValues = modeArgs?.values().flatMap(v => Array.from(v)) ?? [];
102
125
  if (info.ignoreIf === 'mode-only-read' && modeValues.every(m => m && readOnlyModes.has(m))) {
103
126
  // all modes are read-only, so we can ignore this
104
127
  return [];
@@ -108,6 +131,9 @@ function getResults(queries, { dataflow, config, normalize }, results, kind, fun
108
131
  return [];
109
132
  }
110
133
  }
134
+ else if (ignoreOnArgVal()) {
135
+ return [];
136
+ }
111
137
  const results = [];
112
138
  for (const [arg, values] of foundValues.entries()) {
113
139
  for (const value of values) {
@@ -117,7 +143,7 @@ function getResults(queries, { dataflow, config, normalize }, results, kind, fun
117
143
  functionName: vertex.name,
118
144
  lexemeOfArgument: getLexeme(value, arg),
119
145
  linkedIds: linked?.length ? linked : undefined,
120
- value: value ?? defaultValue,
146
+ value: value ?? info.defaultValue ?? defaultValue,
121
147
  versionConstraints: dep?.versionConstraints,
122
148
  derivedVersion: dep?.derivedVersion,
123
149
  namespaceInfo: dep?.namespaceInfo
@@ -11,6 +11,7 @@ export declare const Unknown = "unknown";
11
11
  export interface DependencyCategorySettings {
12
12
  queryDisplayName?: string;
13
13
  functions: FunctionInfo[];
14
+ /** this describes the global default value for this category, e.g., 'stdout' for write operations, please be aware, that this can be overwritten by a by-function default value */
14
15
  defaultValue?: string;
15
16
  additionalAnalysis?: (data: BasicQueryData, ignoreDefault: boolean, functions: FunctionInfo[], queryResults: CallContextQueryResult, result: DependencyInfo[]) => AsyncOrSync<void>;
16
17
  }
@@ -18,11 +18,18 @@ export interface FunctionArgInfo {
18
18
  argName?: string;
19
19
  resolveValue?: boolean | 'library';
20
20
  }
21
+ /** Describes a function that may create a dependency */
21
22
  export interface FunctionInfo extends FunctionArgInfo {
23
+ /** Which package does the function belong to */
22
24
  package?: string;
25
+ /** The name of the function */
23
26
  name: string;
27
+ /** links to other function calls to get the dependency from there (e.g., with `sink` for `print`) */
24
28
  linkTo?: DependencyInfoLink[];
25
- ignoreIf?: 'arg-missing' | 'mode-only-read' | 'mode-only-write';
29
+ /** default value for the argument if no binding value is found, please be aware, that categories can define global defaults, these are overwritten by the per-function defaults */
30
+ defaultValue?: string;
31
+ /** the function is not to be counted as a dependency if the argument is missing */
32
+ ignoreIf?: 'arg-missing' | 'mode-only-read' | 'mode-only-write' | 'arg-true' | 'arg-false';
26
33
  /** additional info on arguments - e.g. for the mode flag */
27
34
  additionalArgs?: Record<string, FunctionArgInfo>;
28
35
  }
@@ -14,6 +14,19 @@ exports.WriteFunctions = [
14
14
  { package: 'utils', name: 'write.table', argIdx: 1, argName: 'file', resolveValue: true },
15
15
  { package: 'utils', name: 'write.csv', argIdx: 1, argName: 'file', resolveValue: true },
16
16
  { package: 'base', name: 'saveRDS', argIdx: 1, argName: 'file', resolveValue: true },
17
+ {
18
+ package: 'base',
19
+ name: 'try',
20
+ linkTo: OutputRedirects,
21
+ argIdx: 2,
22
+ argName: 'outFile',
23
+ resolveValue: true,
24
+ defaultValue: 'stderr',
25
+ ignoreIf: 'arg-true',
26
+ additionalArgs: {
27
+ val: { argIdx: 1, argName: 'silent' }
28
+ }
29
+ },
17
30
  // write functions that don't have argIndex are assumed to write to stdout
18
31
  { package: 'base', name: 'print', linkTo: OutputRedirects, resolveValue: true },
19
32
  { package: 'base', name: 'cat', linkTo: OutputRedirects, argName: 'file', resolveValue: true },
@@ -0,0 +1,6 @@
1
+ import type { DoesCallQuery, DoesCallQueryResult } from './does-call-query-format';
2
+ import type { BasicQueryData } from '../../base-query-format';
3
+ /**
4
+ * Execute does call queries on the given analyzer.
5
+ */
6
+ export declare function executeDoesCallQuery({ analyzer }: BasicQueryData, queries: readonly DoesCallQuery[]): Promise<DoesCallQueryResult>;
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeDoesCallQuery = executeDoesCallQuery;
4
+ const log_1 = require("../../../util/log");
5
+ const parse_1 = require("../../../slicing/criterion/parse");
6
+ const built_in_1 = require("../../../dataflow/environments/built-in");
7
+ /**
8
+ * Execute does call queries on the given analyzer.
9
+ */
10
+ async function executeDoesCallQuery({ analyzer }, queries) {
11
+ const start = Date.now();
12
+ const cg = await analyzer.callGraph();
13
+ const idMap = (await analyzer.normalize()).idMap;
14
+ const results = {};
15
+ for (const query of queries) {
16
+ const id = query.queryId ?? JSON.stringify(query);
17
+ if (id in results) {
18
+ log_1.log.warn(`Duplicate query id '${id}' in does-call queries, SKIP.`);
19
+ continue;
20
+ }
21
+ const nodeId = (0, parse_1.tryResolveSliceCriterionToId)(query.call, idMap);
22
+ if (!nodeId) {
23
+ results[id] = false;
24
+ continue;
25
+ }
26
+ const c = makeCallMatcher(query.calls);
27
+ results[id] = findCallersMatchingConstraints(cg, nodeId, c);
28
+ }
29
+ return {
30
+ '.meta': {
31
+ timing: Date.now() - start
32
+ },
33
+ results
34
+ };
35
+ }
36
+ function makeCallMatcher(constraint) {
37
+ switch (constraint.type) {
38
+ case 'calls-id':
39
+ return (vtx) => vtx.id === constraint.id;
40
+ case 'name':
41
+ if (constraint.nameExact) {
42
+ return (vtx) => vtx.name === constraint.name;
43
+ }
44
+ else {
45
+ const regex = new RegExp(constraint.name);
46
+ return (vtx) => 'name' in vtx && vtx.name ? regex.test(vtx.name) : false;
47
+ }
48
+ case 'and': {
49
+ let matchersAndRemain = constraint.calls.map(makeCallMatcher);
50
+ return (vtx, cg) => {
51
+ const matchersToRemove = [];
52
+ for (const matcher of matchersAndRemain) {
53
+ if (matcher(vtx, cg)) {
54
+ matchersToRemove.push(matcher);
55
+ }
56
+ }
57
+ matchersAndRemain = matchersAndRemain.filter(m => !matchersToRemove.includes(m));
58
+ return matchersAndRemain.length === 0;
59
+ };
60
+ }
61
+ case 'or': {
62
+ const matchersOr = constraint.calls.map(makeCallMatcher);
63
+ return (vtx, cg) => matchersOr.some(m => m(vtx, cg));
64
+ }
65
+ default: {
66
+ throw new Error(`Unhandled constraint type ${JSON.stringify(constraint)}`);
67
+ }
68
+ }
69
+ }
70
+ function findCallersMatchingConstraints(cg, start, constraints) {
71
+ const visited = new Set();
72
+ const toVisit = [start];
73
+ while (toVisit.length > 0) {
74
+ const cur = toVisit.pop();
75
+ if (visited.has(cur)) {
76
+ continue;
77
+ }
78
+ visited.add(cur);
79
+ if ((0, built_in_1.isBuiltIn)(cur)) {
80
+ const name = (0, built_in_1.dropBuiltInPrefix)(cur);
81
+ if (constraints({ id: cur, name }, cg)) {
82
+ return { call: start };
83
+ }
84
+ continue;
85
+ }
86
+ const vtx = cg.getVertex(cur);
87
+ if (!vtx) {
88
+ continue;
89
+ }
90
+ // check if matches
91
+ if (constraints(vtx, cg)) {
92
+ return { call: start };
93
+ }
94
+ for (const out of cg.outgoingEdges(cur) ?? []) {
95
+ toVisit.push(out[0]);
96
+ }
97
+ }
98
+ return false;
99
+ }
100
+ //# sourceMappingURL=does-call-query-executor.js.map
@@ -0,0 +1,51 @@
1
+ import type { BaseQueryFormat, BaseQueryResult } from '../../base-query-format';
2
+ import Joi from 'joi';
3
+ import type { ParsedQueryLine } from '../../query';
4
+ import { executeDoesCallQuery } from './does-call-query-executor';
5
+ import { type NodeId } from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
6
+ import type { SingleSlicingCriterion } from '../../../slicing/criterion/parse';
7
+ import type { ReplOutput } from '../../../cli/repl/commands/repl-main';
8
+ import type { FlowrConfigOptions } from '../../../config';
9
+ interface CallsIdConstraint {
10
+ readonly type: 'calls-id';
11
+ /** The id of the function being called. */
12
+ readonly id: NodeId;
13
+ }
14
+ interface CallsWithNameConstraint {
15
+ readonly type: 'name';
16
+ /** The name of the function being called. */
17
+ readonly name: string;
18
+ /** Should we match the name exactly, or as a regex? */
19
+ readonly nameExact?: boolean;
20
+ }
21
+ interface CallsConstraints {
22
+ readonly type: 'and' | 'or' | 'one-of';
23
+ readonly calls: readonly CallsConstraint[];
24
+ }
25
+ export type CallsConstraint = CallsIdConstraint | CallsWithNameConstraint | CallsConstraints;
26
+ /**
27
+ * Either checks whether a given function calls another function matching the given constraints,
28
+ * or returns all functions that call any function matching the given constraints.
29
+ */
30
+ export interface DoesCallQuery extends BaseQueryFormat {
31
+ readonly type: 'does-call';
32
+ readonly queryId?: string;
33
+ readonly call: SingleSlicingCriterion;
34
+ readonly calls: CallsConstraint;
35
+ }
36
+ export interface FindAllCallsResult {
37
+ readonly call: NodeId;
38
+ }
39
+ export interface DoesCallQueryResult extends BaseQueryResult {
40
+ /** Results are either false (does not call) or an object with details on the calls made */
41
+ readonly results: Record<string, FindAllCallsResult | false>;
42
+ }
43
+ declare function doesCallQueryLineParser(output: ReplOutput, line: readonly string[], _config: FlowrConfigOptions): ParsedQueryLine<'does-call'>;
44
+ export declare const DoesCallQueryDefinition: {
45
+ readonly executor: typeof executeDoesCallQuery;
46
+ readonly asciiSummarizer: (formatter: import("../../../util/text/ansi").OutputFormatter, processed: import("../../../project/flowr-analyzer").ReadonlyFlowrAnalysisProvider<import("../../../r-bridge/parser").KnownParser>, queryResults: BaseQueryResult, result: string[]) => Promise<boolean>;
47
+ readonly fromLine: typeof doesCallQueryLineParser;
48
+ readonly schema: Joi.ObjectSchema<any>;
49
+ readonly flattenInvolvedNodes: (queryResults: BaseQueryResult) => NodeId[];
50
+ };
51
+ export {};
@@ -0,0 +1,102 @@
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
+ exports.DoesCallQueryDefinition = void 0;
7
+ const ansi_1 = require("../../../util/text/ansi");
8
+ const joi_1 = __importDefault(require("joi"));
9
+ const does_call_query_executor_1 = require("./does-call-query-executor");
10
+ const dfg_1 = require("../../../util/mermaid/dfg");
11
+ const args_1 = require("../../../util/text/args");
12
+ const strings_1 = require("../../../util/text/strings");
13
+ const FormatError = 'Invalid constraint format, expected format "(left:$id/"regex")"';
14
+ /**
15
+ * Parses a constraint from a string argument.
16
+ * Returns the constraint or an error message.
17
+ */
18
+ function constraintParser(argument) {
19
+ if (!argument?.startsWith('(') || !argument.includes(')')) {
20
+ return FormatError + ` (got: "${argument}")`;
21
+ }
22
+ const endBracket = argument.indexOf(')');
23
+ const constrPart = argument.slice(1, endBracket);
24
+ const args = (0, args_1.splitAtEscapeSensitive)(constrPart, true, ':');
25
+ if (args.length !== 2) {
26
+ return FormatError + ` (got ${args.length} parts: ${args.join(', ')})`;
27
+ }
28
+ const [criteria, ...rhs] = args;
29
+ const rhsStr = rhs.join(' ');
30
+ if (rhsStr.startsWith('$')) {
31
+ return {
32
+ call: criteria,
33
+ constraint: {
34
+ type: 'calls-id',
35
+ id: rhsStr.slice(1),
36
+ }
37
+ };
38
+ }
39
+ else {
40
+ const isExact = (0, strings_1.startAndEndsWith)(rhsStr, '"');
41
+ const name = isExact ? rhsStr.slice(1, -1) : rhsStr;
42
+ return {
43
+ call: criteria,
44
+ constraint: {
45
+ type: 'name',
46
+ name: name,
47
+ nameExact: isExact ? true : undefined,
48
+ }
49
+ };
50
+ }
51
+ }
52
+ function doesCallQueryLineParser(output, line, _config) {
53
+ const constraint = constraintParser(line[0]);
54
+ if (!constraint || typeof constraint === 'string') {
55
+ output.stderr(output.formatter.format(`Invalid does-call query format:\n ${constraint}`, { color: 1 /* Colors.Red */, effect: ansi_1.ColorEffect.Foreground, style: 1 /* FontStyles.Bold */ }));
56
+ return { query: [] };
57
+ }
58
+ return {
59
+ query: [
60
+ {
61
+ type: 'does-call',
62
+ queryId: constraint.call + ' (shorthand)',
63
+ call: constraint.call,
64
+ calls: constraint.constraint,
65
+ }
66
+ ],
67
+ rCode: line[1]
68
+ };
69
+ }
70
+ exports.DoesCallQueryDefinition = {
71
+ executor: does_call_query_executor_1.executeDoesCallQuery,
72
+ asciiSummarizer: async (formatter, processed, queryResults, result) => {
73
+ const out = queryResults;
74
+ result.push(`Query: ${(0, ansi_1.bold)('does-call', formatter)} (${out['.meta'].timing.toFixed(0)}ms)`);
75
+ for (const [r, v] of Object.entries(out.results)) {
76
+ const idMap = (await processed.normalize()).idMap;
77
+ result.push(` - ${(0, ansi_1.bold)(r, formatter)} found:`);
78
+ if (v === false) {
79
+ result.push(' - Does not call any matching functions.');
80
+ }
81
+ else {
82
+ const loc = idMap.get(v.call)?.location ?? undefined;
83
+ result.push(` - Call with id ${(0, ansi_1.bold)(String(v.call), formatter)} (${(0, dfg_1.formatRange)(loc)})`);
84
+ }
85
+ }
86
+ return true;
87
+ },
88
+ fromLine: doesCallQueryLineParser,
89
+ schema: joi_1.default.object({
90
+ type: joi_1.default.string().valid('does-call').required().description('The type of the query.'),
91
+ queryId: joi_1.default.string().optional().description('An optional unique identifier for this query, to identify it in the output.'),
92
+ call: joi_1.default.string().description('The function from which calls are being made. This is a slicing criterion that resolves to a function definition node.'),
93
+ calls: joi_1.default.object().required().description('The constraints on which functions are being called. This can be a combination of name-based or id-based constraints, combined with logical operators (and, or, one-of).')
94
+ }).description('Either returns all function definitions alongside whether they are recursive, or just those matching the filters.'),
95
+ flattenInvolvedNodes: (queryResults) => {
96
+ const out = queryResults;
97
+ return Object.entries(out.results).flatMap(([, v]) => {
98
+ return v !== false ? v.call : [];
99
+ });
100
+ }
101
+ };
102
+ //# sourceMappingURL=does-call-query-format.js.map
@@ -13,7 +13,7 @@ function executeFileQuery({ analyzer }, queries) {
13
13
  for (const query of queries) {
14
14
  if (query.matchesPathRegex === undefined && query.roles === undefined) {
15
15
  files = base.map(l => ({
16
- role: l.role,
16
+ roles: l.roles,
17
17
  content: l.content(),
18
18
  path: l.path()
19
19
  }));
@@ -21,19 +21,19 @@ function executeFileQuery({ analyzer }, queries) {
21
21
  }
22
22
  const pathRegex = query.matchesPathRegex ? new RegExp(query.matchesPathRegex) : undefined;
23
23
  for (const file of base) {
24
- const fingerprint = `${file.role}:::${file.path()}`;
24
+ const fingerprint = `${file.roles?.join(':')}:::${file.path()}`;
25
25
  if (foundFingerprints.has(fingerprint)) {
26
26
  continue;
27
27
  }
28
28
  if (pathRegex && !pathRegex.test(file.path())) {
29
29
  continue;
30
30
  }
31
- if (query.roles && !query.roles.includes(file.role ?? '')) {
31
+ if ((query.roles?.length ?? 0) > 0 && !query.roles?.every(r => file.roles?.includes(r))) {
32
32
  continue;
33
33
  }
34
34
  foundFingerprints.add(fingerprint);
35
35
  files.push({
36
- role: file.role,
36
+ roles: file.roles,
37
37
  content: file.content(),
38
38
  path: file.path()
39
39
  });
@@ -12,11 +12,12 @@ import type { CommandCompletions } from '../../../cli/repl/core';
12
12
  */
13
13
  export interface FilesQuery extends BaseQueryFormat {
14
14
  readonly type: 'files';
15
+ /** If you provide roles, only files with all the given roles are returned. Supply multiple queries if you want a union! */
15
16
  readonly roles?: FileRole[];
16
17
  readonly matchesPathRegex?: string;
17
18
  }
18
19
  export interface FileQueryInfo<T = object> {
19
- role?: FileRole;
20
+ roles?: readonly FileRole[];
20
21
  path: string;
21
22
  content: T;
22
23
  }
@@ -85,6 +85,21 @@ function filesQueryCompleter(line, startingNewArg, _config) {
85
85
  }
86
86
  return { completions: [] };
87
87
  }
88
+ function guessProto(obj) {
89
+ if (obj === null || obj === undefined) {
90
+ return undefined;
91
+ }
92
+ else if (typeof obj !== 'object') {
93
+ return typeof obj;
94
+ }
95
+ else if ('prototype' in obj && obj.prototype && typeof obj.prototype === 'object') {
96
+ const proto = obj.prototype;
97
+ if (proto.constructor && typeof proto.constructor.name === 'string') {
98
+ return proto.constructor.name;
99
+ }
100
+ }
101
+ return undefined;
102
+ }
88
103
  exports.FilesQueryDefinition = {
89
104
  executor: files_query_executor_1.executeFileQuery,
90
105
  asciiSummarizer: (formatter, _analyzer, queryResults, result) => {
@@ -92,8 +107,9 @@ exports.FilesQueryDefinition = {
92
107
  result.push(`Query: ${(0, ansi_1.bold)('files', formatter)} (${out['.meta'].timing.toFixed(0)}ms)`);
93
108
  result.push(` ╰ Found ${out.files.length} file${out.files.length === 1 ? '' : 's'}`);
94
109
  for (const f of out.files) {
95
- result.push(` ╰ ${(0, ansi_1.bold)(f.path, formatter)}${f.role ? ` [role: ${f.role}]` : ''}:`);
96
- const summary = summarizeObjectWithLimit(f.content);
110
+ const nam = guessProto(f.content);
111
+ result.push(` ╰ ${(0, ansi_1.bold)(f.path, formatter)}${nam ? ' ' + formatter.format(nam, { effect: ansi_1.ColorEffect.Foreground, color: 7 /* Colors.White */ }) : ''}${f.roles ? ` [role: ${f.roles.join(', ')}]` : ''}:`);
112
+ const summary = summarizeObjectWithLimit(f.content, 250);
97
113
  for (const line of summary.split('\n')) {
98
114
  result.push(` ${line}`);
99
115
  }
@@ -9,12 +9,13 @@ async function executeIdMapQuery({ analyzer }, queries) {
9
9
  if (queries.length !== 1) {
10
10
  log_1.log.warn('Id-Map query expects only up to one query, but got', queries.length);
11
11
  }
12
+ const startTime = Date.now();
13
+ const idMap = (await analyzer.normalize()).idMap;
12
14
  return {
13
15
  '.meta': {
14
- /* there is no sense in measuring a get */
15
- timing: 0
16
+ timing: Date.now() - startTime
16
17
  },
17
- idMap: (await analyzer.normalize()).idMap
18
+ idMap
18
19
  };
19
20
  }
20
21
  //# sourceMappingURL=id-map-query-executor.js.map
@@ -0,0 +1,18 @@
1
+ import type { InspectExceptionQuery, InspectExceptionQueryResult } from './inspect-exception-query-format';
2
+ import type { BasicQueryData } from '../../base-query-format';
3
+ import { type SingleSlicingCriterion } from '../../../slicing/criterion/parse';
4
+ import type { NodeId } from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
5
+ import type { ReadonlyFlowrAnalysisProvider } from '../../../project/flowr-analyzer';
6
+ /**
7
+ * Get the functions to consider in the call graph based on the given queries.
8
+ */
9
+ export declare function getFunctionsToConsiderInCallGraph(queries: readonly {
10
+ filter?: readonly SingleSlicingCriterion[];
11
+ }[], analyzer: ReadonlyFlowrAnalysisProvider, onlyDefinitions?: boolean): Promise<{
12
+ cg: import("../../../dataflow/graph/call-graph").CallGraph;
13
+ fns: IteratorObject<[NodeId, Required<import("../../../dataflow/graph/vertex").DataflowGraphVertexFunctionCall | import("../../../dataflow/graph/vertex").DataflowGraphVertexFunctionDefinition>], undefined, unknown>;
14
+ }>;
15
+ /**
16
+ * Execute exception function inspection queries on the given analyzer.
17
+ */
18
+ export declare function executeExceptionQuery({ analyzer }: BasicQueryData, queries: readonly InspectExceptionQuery[]): Promise<InspectExceptionQueryResult>;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFunctionsToConsiderInCallGraph = getFunctionsToConsiderInCallGraph;
4
+ exports.executeExceptionQuery = executeExceptionQuery;
5
+ const parse_1 = require("../../../slicing/criterion/parse");
6
+ const vertex_1 = require("../../../dataflow/graph/vertex");
7
+ const exceptions_of_function_1 = require("../../../dataflow/fn/exceptions-of-function");
8
+ /**
9
+ * Get the functions to consider in the call graph based on the given queries.
10
+ */
11
+ async function getFunctionsToConsiderInCallGraph(queries, analyzer, onlyDefinitions = true) {
12
+ let filters = undefined;
13
+ // filter will remain undefined if at least one of the queries wants all functions
14
+ for (const q of queries) {
15
+ if (q.filter === undefined) {
16
+ filters = undefined;
17
+ break;
18
+ }
19
+ else {
20
+ filters ??= [];
21
+ filters = filters.concat(q.filter);
22
+ }
23
+ }
24
+ const ast = await analyzer.normalize();
25
+ const filterFor = new Set();
26
+ if (filters) {
27
+ for (const f of filters) {
28
+ const i = (0, parse_1.tryResolveSliceCriterionToId)(f, ast.idMap);
29
+ if (i !== undefined) {
30
+ filterFor.add(i);
31
+ }
32
+ }
33
+ }
34
+ const cg = await analyzer.callGraph();
35
+ const fns = (onlyDefinitions || filterFor.size === 0 ? cg.verticesOfType(vertex_1.VertexType.FunctionDefinition) : cg.vertices(true))
36
+ .filter(([, v]) => filterFor.size === 0 || filterFor.has(v.id));
37
+ return { cg, fns };
38
+ }
39
+ /**
40
+ * Execute exception function inspection queries on the given analyzer.
41
+ */
42
+ async function executeExceptionQuery({ analyzer }, queries) {
43
+ const start = Date.now();
44
+ const { cg, fns } = await getFunctionsToConsiderInCallGraph(queries, analyzer);
45
+ const result = {};
46
+ for (const [id,] of fns) {
47
+ result[id] = (0, exceptions_of_function_1.calculateExceptionsOfFunction)(id, cg);
48
+ }
49
+ return {
50
+ '.meta': {
51
+ timing: Date.now() - start
52
+ },
53
+ exceptions: result
54
+ };
55
+ }
56
+ //# sourceMappingURL=inspect-exception-query-executor.js.map
@@ -0,0 +1,34 @@
1
+ import type { BaseQueryFormat, BaseQueryResult } from '../../base-query-format';
2
+ import Joi from 'joi';
3
+ import type { ParsedQueryLine } from '../../query';
4
+ import { executeExceptionQuery } from './inspect-exception-query-executor';
5
+ import { type NodeId } from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
6
+ import type { ReplOutput } from '../../../cli/repl/commands/repl-main';
7
+ import type { FlowrConfigOptions } from '../../../config';
8
+ import type { ExceptionPoint } from '../../../dataflow/fn/exceptions-of-function';
9
+ import type { SingleSlicingCriterion } from '../../../slicing/criterion/parse';
10
+ /**
11
+ * Either returns all function definitions alongside exception information,
12
+ * or just those matching the filters.
13
+ */
14
+ export interface InspectExceptionQuery extends BaseQueryFormat {
15
+ readonly type: 'inspect-exception';
16
+ /** If given, only function definitions that match one of the given slicing criteria are considered. */
17
+ readonly filter?: SingleSlicingCriterion[];
18
+ }
19
+ export interface InspectExceptionQueryResult extends BaseQueryResult {
20
+ /**
21
+ * If a function throws exceptions, the Ids of the throwing functions (at least the functions flowr knows about).
22
+ * An empty array means the function does not throw any exceptions.
23
+ */
24
+ readonly exceptions: Record<NodeId, ExceptionPoint[]>;
25
+ }
26
+ declare function inspectExceptionLineParser(_output: ReplOutput, line: readonly string[], _config: FlowrConfigOptions): ParsedQueryLine<'inspect-exception'>;
27
+ export declare const InspectExceptionQueryDefinition: {
28
+ readonly executor: typeof executeExceptionQuery;
29
+ readonly asciiSummarizer: (formatter: import("../../../util/text/ansi").OutputFormatter, processed: import("../../../project/flowr-analyzer").ReadonlyFlowrAnalysisProvider<import("../../../r-bridge/parser").KnownParser>, queryResults: BaseQueryResult, result: string[]) => Promise<boolean>;
30
+ readonly fromLine: typeof inspectExceptionLineParser;
31
+ readonly schema: Joi.ObjectSchema<any>;
32
+ readonly flattenInvolvedNodes: (queryResults: BaseQueryResult) => NodeId[];
33
+ };
34
+ export {};