@eagleoutice/flowr 2.2.10 → 2.2.11

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 (46) hide show
  1. package/README.md +4 -4
  2. package/cli/repl/server/compact.d.ts +2 -2
  3. package/cli/repl/server/compact.js +3 -3
  4. package/cli/repl/server/messages/message-analysis.d.ts +2 -2
  5. package/cli/repl/server/messages/message-analysis.js +2 -2
  6. package/config.d.ts +9 -0
  7. package/config.js +8 -2
  8. package/dataflow/environments/default-builtin-config.js +3 -1
  9. package/dataflow/environments/environment.js +4 -2
  10. package/dataflow/extractor.d.ts +1 -1
  11. package/dataflow/extractor.js +8 -6
  12. package/dataflow/graph/edge.js +4 -1
  13. package/dataflow/graph/graph.d.ts +6 -0
  14. package/dataflow/graph/graph.js +13 -0
  15. package/dataflow/internal/process/functions/call/argument/unpack-argument.d.ts +3 -0
  16. package/dataflow/internal/process/functions/call/argument/unpack-argument.js +4 -10
  17. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +4 -3
  18. package/dataflow/internal/process/functions/call/built-in/built-in-pipe.js +21 -3
  19. package/dataflow/internal/process/functions/call/built-in/built-in-source.js +4 -2
  20. package/dataflow/processor.d.ts +7 -7
  21. package/documentation/data/server/doc-data-server-messages.js +2 -2
  22. package/documentation/print-interface-wiki.js +3 -0
  23. package/documentation/print-query-wiki.js +20 -1
  24. package/package.json +3 -3
  25. package/queries/catalog/call-context-query/call-context-query-executor.js +13 -0
  26. package/queries/catalog/call-context-query/call-context-query-format.d.ts +4 -0
  27. package/queries/catalog/call-context-query/call-context-query-format.js +1 -0
  28. package/queries/catalog/location-map-query/location-map-query-executor.d.ts +1 -1
  29. package/queries/catalog/location-map-query/location-map-query-executor.js +38 -3
  30. package/queries/catalog/location-map-query/location-map-query-format.d.ts +10 -1
  31. package/queries/catalog/location-map-query/location-map-query-format.js +5 -1
  32. package/queries/catalog/project-query/project-query-executor.d.ts +3 -0
  33. package/queries/catalog/project-query/project-query-executor.js +17 -0
  34. package/queries/catalog/project-query/project-query-format.d.ts +67 -0
  35. package/queries/catalog/project-query/project-query-format.js +26 -0
  36. package/queries/query.d.ts +60 -1
  37. package/queries/query.js +3 -1
  38. package/slicing/static/fingerprint.js +8 -1
  39. package/slicing/static/slice-call.d.ts +1 -1
  40. package/slicing/static/slice-call.js +5 -16
  41. package/slicing/static/slicer-types.d.ts +2 -0
  42. package/slicing/static/static-slicer.d.ts +4 -2
  43. package/slicing/static/static-slicer.js +24 -18
  44. package/slicing/static/visiting-queue.d.ts +7 -1
  45. package/slicing/static/visiting-queue.js +20 -6
  46. package/util/version.js +1 -1
package/README.md CHANGED
@@ -48,7 +48,7 @@ It offers a wide variety of features, for example:
48
48
 
49
49
  ```shell
50
50
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
51
- flowR repl using flowR v2.2.9, R v4.4.0 (r-shell engine)
51
+ flowR repl using flowR v2.2.10, R v4.4.0 (r-shell engine)
52
52
  R> :slicer test/testfiles/example.R --criterion "11@sum"
53
53
  ```
54
54
 
@@ -95,7 +95,7 @@ It offers a wide variety of features, for example:
95
95
 
96
96
 
97
97
  * 🚀 **fast data- and control-flow graphs**\
98
- Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">122.2 ms</span></i> (as of Feb 21, 2025),
98
+ Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">119.1 ms</span></i> (as of Feb 23, 2025),
99
99
  _flowR_ can analyze the data- and control-flow of the average real-world R script. See the [benchmarks](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark) for more information,
100
100
  and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/Dataflow-Graph) for more details on the dataflow graph.
101
101
 
@@ -131,7 +131,7 @@ It offers a wide variety of features, for example:
131
131
 
132
132
  ```shell
133
133
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
134
- flowR repl using flowR v2.2.9, R v4.4.0 (r-shell engine)
134
+ flowR repl using flowR v2.2.10, R v4.4.0 (r-shell engine)
135
135
  R> :dataflow* test/testfiles/example.R
136
136
  ```
137
137
 
@@ -377,7 +377,7 @@ It offers a wide variety of features, for example:
377
377
  ```
378
378
 
379
379
 
380
- (The analysis required _21.60 ms_ (including parse and normalize, using the [r-shell](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
380
+ (The analysis required _21.37 ms_ (including parse and normalize, using the [r-shell](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
381
381
 
382
382
 
383
383
 
@@ -1,2 +1,2 @@
1
- export declare function compact(obj: unknown): Buffer;
2
- export declare function uncompact(buf: Buffer | Uint8Array): unknown;
1
+ export declare function compact(obj: unknown): string;
2
+ export declare function uncompact(buf: string): unknown;
@@ -3,11 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.compact = compact;
4
4
  exports.uncompact = uncompact;
5
5
  const json_1 = require("../../../util/json");
6
- const msgpack_1 = require("@msgpack/msgpack");
6
+ const lz_string_1 = require("lz-string");
7
7
  function compact(obj) {
8
- return Buffer.from((0, msgpack_1.encode)(JSON.parse(JSON.stringify(obj, json_1.jsonReplacer))));
8
+ return (0, lz_string_1.compressToUTF16)(JSON.stringify(obj, json_1.jsonReplacer));
9
9
  }
10
10
  function uncompact(buf) {
11
- return (0, msgpack_1.decode)(new Uint8Array(Buffer.from(buf)));
11
+ return JSON.parse((0, lz_string_1.decompressFromUTF16)(buf));
12
12
  }
13
13
  //# sourceMappingURL=compact.js.map
@@ -69,11 +69,11 @@ export interface FileAnalysisResponseMessageCompact extends IdMessageBase {
69
69
  /**
70
70
  * See the {@link PipelineExecutor} and {@link DEFAULT_DATAFLOW_PIPELINE} for details on the results.
71
71
  */
72
- results: Buffer;
72
+ results: string;
73
73
  /**
74
74
  * Only if the {@link FileAnalysisRequestMessage} contained a `cfg: true` this will contain the {@link ControlFlowInformation} of the file.
75
75
  */
76
- cfg?: Buffer;
76
+ cfg?: string;
77
77
  }
78
78
  /**
79
79
  * Similar to {@link FileAnalysisResponseMessageJson} but using n-quads as serialization format.
@@ -29,8 +29,8 @@ const compactSchema = joi_1.default.object({
29
29
  type: joi_1.default.string().valid('response-file-analysis').required().description('The type of the message.'),
30
30
  id: joi_1.default.string().optional().description('The id of the message, if you passed one in the request.'),
31
31
  format: joi_1.default.string().valid('bson').required().description('The format of the results in bson format.'),
32
- results: joi_1.default.binary().required().description('The results of the analysis (one field per step).'),
33
- cfg: joi_1.default.binary().optional().description('The control flow information of the file, only present if requested.')
32
+ results: joi_1.default.string().required().description('The results of the analysis (one field per step).'),
33
+ cfg: joi_1.default.string().optional().description('The control flow information of the file, only present if requested.')
34
34
  });
35
35
  const nquadsSchema = joi_1.default.object({
36
36
  type: joi_1.default.string().valid('response-file-analysis').required().description('The type of the message.'),
package/config.d.ts CHANGED
@@ -97,6 +97,15 @@ export interface FlowrConfigOptions extends MergeableRecord {
97
97
  */
98
98
  readonly dropPaths: DropPathsOption;
99
99
  };
100
+ /**
101
+ * The configuration for flowR's slicer
102
+ */
103
+ slicer?: {
104
+ /**
105
+ * The maximum number of iterations to perform on a single function call during slicing
106
+ */
107
+ readonly threshold?: number;
108
+ };
100
109
  };
101
110
  }
102
111
  export interface TreeSitterEngineConfig extends MergeableRecord {
package/config.js CHANGED
@@ -70,12 +70,15 @@ exports.defaultConfigOptions = {
70
70
  defaultEngine: 'r-shell',
71
71
  solver: {
72
72
  variables: VariableResolve.Alias,
73
- pointerTracking: true,
73
+ pointerTracking: false,
74
74
  resolveSource: {
75
75
  dropPaths: DropPathsOption.No,
76
76
  ignoreCapitalization: true,
77
77
  inferWorkingDirectory: InferWorkingDirectory.ActiveScript,
78
78
  searchPath: []
79
+ },
80
+ slicer: {
81
+ threshold: 50
79
82
  }
80
83
  }
81
84
  };
@@ -107,7 +110,10 @@ exports.flowrConfigFileSchema = joi_1.default.object({
107
110
  ignoreCapitalization: joi_1.default.boolean().description('Search for filenames matching in the lowercase.'),
108
111
  inferWorkingDirectory: joi_1.default.string().valid(...Object.values(InferWorkingDirectory)).description('Try to infer the working directory from the main or any script to analyze.'),
109
112
  searchPath: joi_1.default.array().items(joi_1.default.string()).description('Additionally search in these paths.')
110
- }).optional().description('If lax source calls are active, flowR searches for sourced files much more freely, based on the configurations you give it. This option is only in effect if `ignoreSourceCalls` is set to false.')
113
+ }).optional().description('If lax source calls are active, flowR searches for sourced files much more freely, based on the configurations you give it. This option is only in effect if `ignoreSourceCalls` is set to false.'),
114
+ slicer: joi_1.default.object({
115
+ threshold: joi_1.default.number().optional().description('The maximum number of iterations to perform on a single function call during slicing.')
116
+ }).optional().description('The configuration for the slicer.')
111
117
  }).description('How to resolve constants, constraints, cells, ...')
112
118
  }).description('The configuration file format for flowR.');
113
119
  // we don't load from a config file at all by default unless setConfigFile is called
@@ -134,7 +134,9 @@ exports.DefaultBuiltinConfig = [
134
134
  /* downloader and installer functions (R, devtools, BiocManager) */
135
135
  'library.dynam', 'install.packages', 'install', 'install_github', 'install_gitlab', 'install_bitbucket', 'install_url', 'install_git', 'install_svn', 'install_local', 'install_version', 'update_packages',
136
136
  /* weird env attachments */
137
- 'attach', 'unname', 'data'
137
+ 'attach', 'unname', 'data',
138
+ /* file creation/removal */
139
+ 'dir.create', 'dir_create', 'Sys.chmod', 'unlink', 'file.remove', 'file.rename', 'file.copy', 'file.link', 'file.append', 'Sys.junction'
138
140
  ],
139
141
  processor: 'builtin:default',
140
142
  config: { hasUnknownSideEffects: true },
@@ -81,8 +81,10 @@ exports.EmptyBuiltInEnvironment = {
81
81
  * See {@link EmptyBuiltInEnvironment} for the case `fullBuiltIns = false`.
82
82
  */
83
83
  function initializeCleanEnvironments(fullBuiltIns = true) {
84
- exports.BuiltInEnvironment.memory ??= built_in_1.BuiltInMemory;
85
- exports.EmptyBuiltInEnvironment.memory ??= built_in_1.EmptyBuiltInMemory;
84
+ if (exports.BuiltInEnvironment.memory === undefined) {
85
+ exports.BuiltInEnvironment.memory = built_in_1.BuiltInMemory;
86
+ exports.EmptyBuiltInEnvironment.memory = built_in_1.EmptyBuiltInMemory;
87
+ }
86
88
  return {
87
89
  current: new Environment(fullBuiltIns ? exports.BuiltInEnvironment : exports.EmptyBuiltInEnvironment),
88
90
  level: 0
@@ -14,4 +14,4 @@ export declare const processors: DataflowProcessors<ParentInformation>;
14
14
  * (e.g., in the event of a `source` call).
15
15
  * For the actual, canonical fold entry point, see {@link processDataflowFor}.
16
16
  */
17
- export declare function produceDataFlowGraph<OtherInfo>(parser: Parser<KnownParserType>, request: RParseRequests, ast: NormalizedAst<OtherInfo & ParentInformation>): DataflowInformation;
17
+ export declare function produceDataFlowGraph<OtherInfo>(parser: Parser<KnownParserType>, request: RParseRequests, completeAst: NormalizedAst<OtherInfo & ParentInformation>): DataflowInformation;
@@ -80,9 +80,9 @@ function resolveLinkToSideEffects(ast, graph) {
80
80
  * (e.g., in the event of a `source` call).
81
81
  * For the actual, canonical fold entry point, see {@link processDataflowFor}.
82
82
  */
83
- function produceDataFlowGraph(parser, request, ast) {
84
- const multifile = Array.isArray(request);
83
+ function produceDataFlowGraph(parser, request, completeAst) {
85
84
  let firstRequest;
85
+ const multifile = Array.isArray(request);
86
86
  if (multifile) {
87
87
  firstRequest = request[0];
88
88
  }
@@ -90,23 +90,25 @@ function produceDataFlowGraph(parser, request, ast) {
90
90
  firstRequest = request;
91
91
  }
92
92
  const dfData = {
93
- parser: parser,
94
- completeAst: ast,
93
+ parser,
94
+ completeAst,
95
95
  environment: (0, environment_1.initializeCleanEnvironments)(),
96
96
  processors: exports.processors,
97
97
  currentRequest: firstRequest,
98
98
  controlDependencies: undefined,
99
99
  referenceChain: [firstRequest],
100
100
  };
101
- let df = (0, processor_1.processDataflowFor)(ast.ast, dfData);
101
+ let df = (0, processor_1.processDataflowFor)(completeAst.ast, dfData);
102
+ df.graph.sourced.unshift(firstRequest.request === 'file' ? firstRequest.content : '<inline>');
102
103
  if (multifile) {
103
104
  for (let i = 1; i < request.length; i++) {
105
+ /* source requests register automatically */
104
106
  df = (0, built_in_source_1.standaloneSourceFile)(request[i], dfData, `root-${i}`, df);
105
107
  }
106
108
  }
107
109
  // finally, resolve linkages
108
110
  (0, built_in_function_definition_1.updateNestedFunctionCalls)(df.graph, df.environment);
109
- resolveLinkToSideEffects(ast, df.graph);
111
+ resolveLinkToSideEffects(completeAst, df.graph);
110
112
  return df;
111
113
  }
112
114
  //# sourceMappingURL=extractor.js.map
@@ -98,7 +98,10 @@ function edgeDoesNotIncludeType(type, types) {
98
98
  const alwaysTraverseEdgeTypes = EdgeType.Reads | EdgeType.DefinedBy | EdgeType.Argument | EdgeType.Calls;
99
99
  const definedByOnCallTypes = EdgeType.DefinesOnCall | EdgeType.DefinedByOnCall;
100
100
  function shouldTraverseEdge(types) {
101
- if (edgeIncludesType(types, alwaysTraverseEdgeTypes)) {
101
+ if (edgeIncludesType(types, EdgeType.NonStandardEvaluation)) {
102
+ return 0 /* TraverseEdge.Never */;
103
+ }
104
+ else if (edgeIncludesType(types, alwaysTraverseEdgeTypes)) {
102
105
  return 3 /* TraverseEdge.Always */;
103
106
  }
104
107
  else if (edgeIncludesType(types, definedByOnCallTypes)) {
@@ -73,6 +73,7 @@ export interface DataflowGraphJson {
73
73
  readonly rootVertices: NodeId[];
74
74
  readonly vertexInformation: [NodeId, DataflowGraphVertexInfo][];
75
75
  readonly edgeInformation: [NodeId, [NodeId, DataflowGraphEdge][]][];
76
+ readonly sourced?: (string | '<inline>')[];
76
77
  }
77
78
  /**
78
79
  * An unknown side effect describes something that we cannot handle correctly (in all cases).
@@ -103,6 +104,8 @@ export type UnknownSidEffect = NodeId | {
103
104
  export declare class DataflowGraph<Vertex extends DataflowGraphVertexInfo = DataflowGraphVertexInfo, Edge extends DataflowGraphEdge = DataflowGraphEdge> {
104
105
  private static DEFAULT_ENVIRONMENT;
105
106
  private _idMap;
107
+ /** all file paths included in this dfg */
108
+ private _sourced;
106
109
  private readonly _unknownSideEffects;
107
110
  constructor(idMap: AstIdMap | undefined);
108
111
  /** Contains the vertices of the root level graph (i.e., included those vertices from the complete graph, that are nested within function definitions) */
@@ -135,6 +138,9 @@ export declare class DataflowGraph<Vertex extends DataflowGraphVertexInfo = Data
135
138
  ingoingEdges(id: NodeId): IngoingEdges | undefined;
136
139
  /** Retrieves the id-map to the normalized AST attached to the dataflow graph */
137
140
  get idMap(): AstIdMap | undefined;
141
+ get sourced(): (string | '<inline>')[];
142
+ /** Mark this file as being part of the dfg */
143
+ addFile(source: string | '<inline>'): void;
138
144
  /**
139
145
  * Retrieves the set of vertices which have side effects that we do not know anything about.
140
146
  */
@@ -55,6 +55,8 @@ function getReferenceOfArgument(arg) {
55
55
  class DataflowGraph {
56
56
  static DEFAULT_ENVIRONMENT = undefined;
57
57
  _idMap;
58
+ /** all file paths included in this dfg */
59
+ _sourced = [];
58
60
  /*
59
61
  * Set of vertices which have sideEffects that we do not know anything about.
60
62
  * As a (temporary) solution until we have FD edges, a side effect may also store known target links
@@ -113,6 +115,13 @@ class DataflowGraph {
113
115
  get idMap() {
114
116
  return this._idMap;
115
117
  }
118
+ get sourced() {
119
+ return this._sourced;
120
+ }
121
+ /** Mark this file as being part of the dfg */
122
+ addFile(source) {
123
+ this._sourced.push(source);
124
+ }
116
125
  /**
117
126
  * Retrieves the set of vertices which have side effects that we do not know anything about.
118
127
  */
@@ -235,6 +244,7 @@ class DataflowGraph {
235
244
  this.rootVertices.add(root);
236
245
  }
237
246
  }
247
+ this.sourced.push(...otherGraph.sourced);
238
248
  for (const unknown of otherGraph.unknownSideEffects) {
239
249
  this._unknownSideEffects.add(unknown);
240
250
  }
@@ -326,6 +336,9 @@ class DataflowGraph {
326
336
  graph.rootVertices = new Set(data.rootVertices);
327
337
  graph.vertexInformation = new Map(data.vertexInformation);
328
338
  graph.edgeInformation = new Map(data.edgeInformation.map(([id, edges]) => [id, new Map(edges)]));
339
+ if (data.sourced) {
340
+ graph._sourced = data.sourced;
341
+ }
329
342
  return graph;
330
343
  }
331
344
  }
@@ -1,3 +1,6 @@
1
1
  import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
2
2
  import type { RNode } from '../../../../../../r-bridge/lang-4.x/ast/model/model';
3
+ /**
4
+ * Retrieve the value from an argument, if it is not empty.
5
+ */
3
6
  export declare function unpackArgument<OtherInfo>(arg: RFunctionArgument<OtherInfo>, noNameOnly?: boolean): RNode<OtherInfo> | undefined;
@@ -1,17 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.unpackArgument = unpackArgument;
4
- const log_1 = require("../../../../../../util/log");
5
4
  const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
5
+ /**
6
+ * Retrieve the value from an argument, if it is not empty.
7
+ */
6
8
  function unpackArgument(arg, noNameOnly = true) {
7
- if (arg === r_function_call_1.EmptyArgument) {
8
- log_1.log.trace('Argument is empty, skipping');
9
- return undefined;
10
- }
11
- else if (noNameOnly && arg.name !== undefined) {
12
- log_1.log.trace(`Argument ${JSON.stringify(arg)} is not unnamed, skipping`);
13
- return undefined;
14
- }
15
- return arg.value;
9
+ return arg === r_function_call_1.EmptyArgument || (noNameOnly && arg.name) ? undefined : arg.value;
16
10
  }
17
11
  //# sourceMappingURL=unpack-argument.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);
@@ -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
  }
@@ -136,7 +136,7 @@ function processSourceCall(name, args, rootId, data, config) {
136
136
  const request = sourceProvider.createRequest(filepath);
137
137
  // check if the sourced file has already been dataflow analyzed, and if so, skip it
138
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`);
139
+ logger_1.dataflowLogger.warn(`Found loop in dataflow analysis for ${JSON.stringify(request)}: ${JSON.stringify(data.referenceChain)}, skipping further dataflow analysis`);
140
140
  information.graph.markIdForUnknownSideEffects(rootId);
141
141
  return information;
142
142
  }
@@ -172,7 +172,8 @@ function sourceRequest(rootId, request, data, information, getId) {
172
172
  });
173
173
  }
174
174
  catch (e) {
175
- logger_1.dataflowLogger.warn(`Failed to analyze sourced file ${JSON.stringify(request)}, skipping: ${e.message}`);
175
+ logger_1.dataflowLogger.error(`Failed to analyze sourced file ${JSON.stringify(request)}, skipping: ${e.message}`);
176
+ logger_1.dataflowLogger.error(e.stack);
176
177
  information.graph.markIdForUnknownSideEffects(rootId);
177
178
  return information;
178
179
  }
@@ -183,6 +184,7 @@ function sourceRequest(rootId, request, data, information, getId) {
183
184
  for (const out of dataflow.out) {
184
185
  dataflow.graph.addControlDependency(out.nodeId, rootId, true);
185
186
  }
187
+ dataflow.graph.addFile(request.request === 'file' ? request.content : '<inline>');
186
188
  // update our graph with the sourced file's information
187
189
  const newInformation = { ...information };
188
190
  newInformation.environment = (0, overwrite_1.overwriteEnvironment)(information.environment, dataflow.environment);
@@ -10,17 +10,17 @@ import type { KnownParserType, Parser } from '../r-bridge/parser';
10
10
  export interface DataflowProcessorInformation<OtherInfo> {
11
11
  readonly parser: Parser<KnownParserType>;
12
12
  /**
13
- * Initial and frozen ast-information
14
- */
13
+ * Initial and frozen ast-information
14
+ */
15
15
  readonly completeAst: NormalizedAst<OtherInfo>;
16
16
  /**
17
- * Correctly contains pushed local scopes introduced by `function` scopes.
18
- * Will by default *not* contain any symbol-bindings introduced along the way; they have to be decorated when moving up the tree.
19
- */
17
+ * Correctly contains pushed local scopes introduced by `function` scopes.
18
+ * Will by default *not* contain any symbol-bindings introduced along the way; they have to be decorated when moving up the tree.
19
+ */
20
20
  readonly environment: REnvironmentInformation;
21
21
  /**
22
- * Other processors to be called by the given functions
23
- */
22
+ * Other processors to be called by the given functions
23
+ */
24
24
  readonly processors: DataflowProcessors<OtherInfo>;
25
25
  /**
26
26
  * The {@link RParseRequests} that is currently being parsed
@@ -201,10 +201,10 @@ While the context is derived from the \`filename\`, we currently offer no way to
201
201
  })}
202
202
 
203
203
  <a id="analysis-format-compact"></a>
204
- **Retrieve the Output in a compactedForm**
204
+ **Retrieve the Output in a Compacted Form**
205
205
 
206
206
  The default response is formatted as JSON. But this can get very big quickly.
207
- By specifying \`format: "compact"\`, you can retrieve the results heavily compacted (using [msgpack](https://www.npmjs.com/package/@msgpack/msgpack)).
207
+ By specifying \`format: "compact"\`, you can retrieve the results heavily compacted (using [lz-string](https://www.npmjs.com/package/lz-string)).
208
208
  This works with and without the control flow graph as described [above](#analysis-include-cfg).
209
209
 
210
210
  ${await (0, doc_server_message_1.documentServerMessageResponse)({
@@ -209,6 +209,9 @@ ${(0, doc_code_1.codeBlock)('json', JSON.stringify({
209
209
  ignoreCapitalization: true,
210
210
  inferWorkingDirectory: config_1.InferWorkingDirectory.ActiveScript,
211
211
  searchPath: []
212
+ },
213
+ slicer: {
214
+ threshold: 50
212
215
  }
213
216
  }
214
217
  }, null, 2))}
@@ -62,6 +62,7 @@ It's also possible to filter the results based on the following properties:
62
62
  The \`fileFilter\` property is an object made up of two properties:
63
63
  - **Filter** (\`filter\`): A regular expression that a node's file attribute must match to be considered.
64
64
  - **Include Undefined Files** (\`includeUndefinedFiles\`): If \`fileFilter\` is set, but a node's file attribute is not present, should we include it in the results? Defaults to \`true\`.
65
+ 2. **Ignore Parameter Values** (\`ignoreParameterValues\`): Should we ignore default values for parameters in the results?
65
66
 
66
67
  Re-using the example code from above, the following query attaches all calls to \`mean\` to the kind \`visualize\` and the subkind \`text\`,
67
68
  all calls that start with \`read_\` to the kind \`input\` but only if they are not locally overwritten, and the subkind \`csv-file\`, and links all calls to \`points\` to the last call to \`plot\`:
@@ -122,6 +123,24 @@ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
122
123
  `;
123
124
  }
124
125
  });
126
+ (0, doc_query_1.registerQueryDocumentation)('project', {
127
+ name: 'Project Query',
128
+ type: 'active',
129
+ shortDescription: 'Returns information about the analyzed project',
130
+ functionName: dataflow_query_executor_1.executeDataflowQuery.name,
131
+ functionFile: '../queries/catalog/project-query/project-query-executor.ts',
132
+ buildExplanation: async (shell) => {
133
+ const exampleCode = 'x + 1';
134
+ return `
135
+ This query returns the information about the analyzed project.
136
+ Currently, this is only the list of file paths included.
137
+
138
+ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
139
+ type: 'project'
140
+ }], { showCode: true, collapseQuery: true })}
141
+ `;
142
+ }
143
+ });
125
144
  (0, doc_query_1.registerQueryDocumentation)('normalized-ast', {
126
145
  name: 'Normalized AST Query',
127
146
  type: 'active',
@@ -464,7 +483,7 @@ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
464
483
  type: 'location-map'
465
484
  }], { showCode: false, collapseQuery: true })}
466
485
 
467
- All locations are given as a ${(0, doc_types_1.shortLink)('SourceRange', types.info)} in the format \`[start-line, start-column, end-line, end-column]\`.
486
+ All locations are given as a ${(0, doc_types_1.shortLink)('SourceRange', types.info)} paired with the file id in the format \`[file-id, [start-line, start-column, end-line, end-column]]\`.
468
487
 
469
488
  `;
470
489
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.2.10",
3
+ "version": "2.2.11",
4
4
  "description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "repository": {
@@ -36,7 +36,7 @@
36
36
  "gen:readme": "ts-node src/documentation/print-readme.ts",
37
37
  "build": "tsc --project .",
38
38
  "build-dev": "npm run build && npm run build:copy-wasm",
39
- "build:bundle-flowr": "npm run build && esbuild --bundle dist/src/cli/flowr.js --platform=node --bundle --minify --external:clipboardy --target=node22 --outfile=dist/src/cli/flowr.min.js && npm run build:copy-wasm-min",
39
+ "build:bundle-flowr": "npm run build && esbuild --bundle dist/src/cli/flowr.js --platform=node --tree-shaking=true --bundle --minify --external:clipboardy --target=node22 --outfile=dist/src/cli/flowr.min.js && npm run build:copy-wasm-min",
40
40
  "build:copy-wasm": "mkdir -p dist/src/r-bridge/lang-4.x/tree-sitter/ && cp src/r-bridge/lang-4.x/tree-sitter/tree-sitter-r.wasm src/r-bridge/lang-4.x/tree-sitter/tree-sitter.wasm dist/src/r-bridge/lang-4.x/tree-sitter/",
41
41
  "build:copy-wasm-min": "mkdir -p dist/src/cli && cp src/r-bridge/lang-4.x/tree-sitter/tree-sitter-r.wasm src/r-bridge/lang-4.x/tree-sitter/tree-sitter.wasm dist/src/cli",
42
42
  "lint-local": "npx eslint --version && npx eslint src/ test/ --rule \"no-warning-comments: off\"",
@@ -199,12 +199,12 @@
199
199
  "vitest": "^3.0.6"
200
200
  },
201
201
  "dependencies": {
202
- "@msgpack/msgpack": "^3.0.1",
203
202
  "@xmldom/xmldom": "^0.9.7",
204
203
  "clipboardy": "^4.0.0",
205
204
  "command-line-args": "^6.0.1",
206
205
  "command-line-usage": "^7.0.3",
207
206
  "joi": "^17.13.3",
207
+ "lz-string": "^1.5.0",
208
208
  "n-readlines": "^1.0.1",
209
209
  "n3": "^1.23.1",
210
210
  "object-hash": "^3.0.0",
@@ -152,6 +152,16 @@ function doesFilepathMatch(file, filter) {
152
152
  }
153
153
  return filter.filter.test(file);
154
154
  }
155
+ function isParameterDefaultValue(nodeId, ast) {
156
+ let node = ast.idMap.get(nodeId);
157
+ while (node !== undefined) {
158
+ if (node.info.role === "param-value" /* RoleInParent.ParameterDefaultValue */) {
159
+ return true;
160
+ }
161
+ node = node.info.parent ? ast.idMap.get(node.info.parent) : undefined;
162
+ }
163
+ return false;
164
+ }
155
165
  /**
156
166
  * Multi-stage call context query resolve.
157
167
  *
@@ -209,6 +219,9 @@ function executeCallContextQueries({ dataflow: { graph }, ast }, queries) {
209
219
  /* if the call is quoted, we do not want to link to it */
210
220
  continue;
211
221
  }
222
+ else if (query.ignoreParameterValues && isParameterDefaultValue(nodeId, ast)) {
223
+ continue;
224
+ }
212
225
  let linkedIds = undefined;
213
226
  if (cfg && isSubCallQuery(query)) {
214
227
  const linked = Array.isArray(query.linkTo) ? query.linkTo : [query.linkTo];
@@ -41,6 +41,10 @@ export interface DefaultCallContextQueryFormat<RegexType extends RegExp | string
41
41
  * Consider a case like `f <- function_of_interest`, do you want uses of `f` to be included in the results?
42
42
  */
43
43
  readonly includeAliases?: boolean;
44
+ /**
45
+ * Should we ignore default values for parameters in the results?
46
+ */
47
+ readonly ignoreParameterValues?: boolean;
44
48
  /**
45
49
  * Filter that, when set, a node's file attribute must match to be considered
46
50
  */
@@ -32,6 +32,7 @@ exports.CallContextQueryDefinition = {
32
32
  kind: joi_1.default.string().optional().description('The kind of the call, this can be used to group calls together (e.g., linking `plot` to `visualize`). Defaults to `.`'),
33
33
  subkind: joi_1.default.string().optional().description('The subkind of the call, this can be used to uniquely identify the respective call type when grouping the output (e.g., the normalized name, linking `ggplot` to `plot`). Defaults to `.`'),
34
34
  callTargets: joi_1.default.string().valid(...Object.values(identify_link_to_last_call_relation_1.CallTargets)).optional().description('Call targets the function may have. This defaults to `any`. Request this specifically to gain all call targets we can resolve.'),
35
+ ignoreParameterValues: joi_1.default.boolean().optional().description('Should we ignore default values for parameters in the results?'),
35
36
  includeAliases: joi_1.default.boolean().optional().description('Consider a case like `f <- function_of_interest`, do you want uses of `f` to be included in the results?'),
36
37
  fileFilter: joi_1.default.object({
37
38
  fileFilter: joi_1.default.string().required().description('Regex that a node\'s file attribute must match to be considered'),
@@ -1,3 +1,3 @@
1
1
  import type { LocationMapQuery, LocationMapQueryResult } from './location-map-query-format';
2
2
  import type { BasicQueryData } from '../../base-query-format';
3
- export declare function executeLocationMapQuery({ ast }: BasicQueryData, queries: readonly LocationMapQuery[]): LocationMapQueryResult;
3
+ export declare function executeLocationMapQuery({ ast, dataflow: { graph } }: BasicQueryData, queries: readonly LocationMapQuery[]): LocationMapQueryResult;