@eagleoutice/flowr 2.2.6 → 2.2.7

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.
@@ -27,7 +27,7 @@ export declare function trackAliasesInGraph(id: NodeId, graph: DataflowGraph, id
27
27
  * Convenience function using the variable resolver as specified within the configuration file
28
28
  * In the future we may want to have this set once at the start of the analysis
29
29
  *
30
- * @see {@link resolve} - for a more general approach which "evaluates" a node based on value resolve
30
+ * @see {@link resolveIdToValue} - for a more general approach which "evaluates" a node based on value resolve
31
31
  */
32
32
  export declare function resolveValueOfVariable(identifier: Identifier | undefined, environment: REnvironmentInformation, idMap?: AstIdMap): unknown[] | undefined;
33
33
  export interface ResolveInfo {
@@ -43,9 +43,10 @@ export interface ResolveInfo {
43
43
  /**
44
44
  * Generalized {@link resolveValueOfVariable} function which evaluates a node based on the value resolve
45
45
  *
46
- * @param id - The node id or node to resolve
46
+ * @param id - The node id or node to resolve
47
47
  * @param environment - The current environment used for name resolution
48
- * @param idMap - The id map to resolve the node if given as an id
49
- * @param full - Whether to track variables
48
+ * @param graph - The graph to resolve in
49
+ * @param idMap - The id map to resolve the node if given as an id
50
+ * @param full - Whether to track variables
50
51
  */
51
- export declare function resolve(id: NodeId | RNodeWithParent, { environment, graph, idMap, full }: ResolveInfo): unknown[] | undefined;
52
+ export declare function resolveIdToValue(id: NodeId | RNodeWithParent, { environment, graph, idMap, full }: ResolveInfo): unknown[] | undefined;
@@ -7,7 +7,7 @@ exports.getAliases = getAliases;
7
7
  exports.trackAliasInEnvironments = trackAliasInEnvironments;
8
8
  exports.trackAliasesInGraph = trackAliasesInGraph;
9
9
  exports.resolveValueOfVariable = resolveValueOfVariable;
10
- exports.resolve = resolve;
10
+ exports.resolveIdToValue = resolveIdToValue;
11
11
  const environment_1 = require("./environment");
12
12
  const logic_1 = require("../../util/logic");
13
13
  const identifier_1 = require("./identifier");
@@ -189,6 +189,20 @@ function trackAliasInEnvironments(identifier, use, idMap) {
189
189
  }
190
190
  return values;
191
191
  }
192
+ function isNestedInLoop(node, ast) {
193
+ const parent = node?.info.parent;
194
+ if (node === undefined || !parent) {
195
+ return false;
196
+ }
197
+ const parentNode = ast.get(parent);
198
+ if (parentNode === undefined) {
199
+ return false;
200
+ }
201
+ if (parentNode.type === type_1.RType.WhileLoop || parentNode.type === type_1.RType.RepeatLoop) {
202
+ return true;
203
+ }
204
+ return isNestedInLoop(parentNode, ast);
205
+ }
192
206
  function trackAliasesInGraph(id, graph, idMap) {
193
207
  idMap ??= graph.idMap;
194
208
  (0, assert_1.guard)(idMap !== undefined, 'The ID map is required to get the lineage of a node');
@@ -198,6 +212,7 @@ function trackAliasesInGraph(id, graph, idMap) {
198
212
  const clean = (0, environment_1.initializeCleanEnvironments)();
199
213
  const cleanFingerprint = (0, fingerprint_1.envFingerprint)(clean);
200
214
  queue.add(id, clean, cleanFingerprint, false);
215
+ let forceBot = false;
201
216
  const resultIds = [];
202
217
  while (queue.nonEmpty()) {
203
218
  const { id, baseEnvironment } = queue.next();
@@ -206,18 +221,36 @@ function trackAliasesInGraph(id, graph, idMap) {
206
221
  continue;
207
222
  }
208
223
  const [vertex, outgoingEdges] = res;
224
+ const cds = vertex.controlDependencies;
225
+ for (const cd of cds ?? []) {
226
+ const target = graph.idMap?.get(cd.id);
227
+ if (target === undefined) {
228
+ continue;
229
+ }
230
+ if (target.type === type_1.RType.WhileLoop || target.type === type_1.RType.RepeatLoop) {
231
+ forceBot = true;
232
+ break;
233
+ }
234
+ }
235
+ if (!forceBot && (cds?.length === 0 && isNestedInLoop(idMap.get(id), idMap))) {
236
+ forceBot = true;
237
+ }
238
+ if (forceBot) {
239
+ break;
240
+ }
209
241
  if (vertex.tag === vertex_1.VertexType.Value) {
210
242
  resultIds.push(id);
211
243
  continue;
212
244
  }
213
245
  // travel all read and defined-by edges
214
246
  for (const [targetId, edge] of outgoingEdges) {
215
- if ((0, edge_1.edgeIncludesType)(edge.types, edge_1.EdgeType.Reads | edge_1.EdgeType.DefinedBy | edge_1.EdgeType.DefinedByOnCall)) {
247
+ // currently, they have to be exact!
248
+ if (edge.types === edge_1.EdgeType.Reads || edge.types === edge_1.EdgeType.DefinedBy || edge.types === edge_1.EdgeType.DefinedByOnCall) {
216
249
  queue.add(targetId, baseEnvironment, cleanFingerprint, false);
217
250
  }
218
251
  }
219
252
  }
220
- if (resultIds.length === 0) {
253
+ if (forceBot || resultIds.length === 0) {
221
254
  return undefined;
222
255
  }
223
256
  const values = [];
@@ -233,7 +266,7 @@ function trackAliasesInGraph(id, graph, idMap) {
233
266
  * Convenience function using the variable resolver as specified within the configuration file
234
267
  * In the future we may want to have this set once at the start of the analysis
235
268
  *
236
- * @see {@link resolve} - for a more general approach which "evaluates" a node based on value resolve
269
+ * @see {@link resolveIdToValue} - for a more general approach which "evaluates" a node based on value resolve
237
270
  */
238
271
  function resolveValueOfVariable(identifier, environment, idMap) {
239
272
  const resolve = (0, config_1.getConfig)().solver.variables;
@@ -247,12 +280,13 @@ function resolveValueOfVariable(identifier, environment, idMap) {
247
280
  /**
248
281
  * Generalized {@link resolveValueOfVariable} function which evaluates a node based on the value resolve
249
282
  *
250
- * @param id - The node id or node to resolve
283
+ * @param id - The node id or node to resolve
251
284
  * @param environment - The current environment used for name resolution
252
- * @param idMap - The id map to resolve the node if given as an id
253
- * @param full - Whether to track variables
285
+ * @param graph - The graph to resolve in
286
+ * @param idMap - The id map to resolve the node if given as an id
287
+ * @param full - Whether to track variables
254
288
  */
255
- function resolve(id, { environment, graph, idMap, full }) {
289
+ function resolveIdToValue(id, { environment, graph, idMap, full }) {
256
290
  idMap ??= graph?.idMap;
257
291
  const node = typeof id === 'object' ? id : idMap?.get(id);
258
292
  if (node === undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.2.6",
3
+ "version": "2.2.7",
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": {
@@ -76,7 +76,7 @@ function executeDependenciesQuery(data, queries) {
76
76
  nodeId: id,
77
77
  functionName: vertex.name,
78
78
  // write functions that don't have argIndex are assumed to write to stdout
79
- destination: value ?? (linkedIds?.length ? dependencies_query_format_1.Unknown : 'stdout'),
79
+ destination: value ?? 'stdout',
80
80
  lexemeOfArgument: getLexeme(value, argId),
81
81
  linkedIds: linkedIds?.length ? linkedIds : undefined
82
82
  }));
@@ -160,7 +160,7 @@ function collectValuesFromLinks(args, data, linkedIds) {
160
160
  }
161
161
  }
162
162
  }
163
- return map;
163
+ return map.size ? map : undefined;
164
164
  }
165
165
  function hasCharacterOnly(data, vertex, idMap) {
166
166
  if (!vertex.args || vertex.args.length === 0 || !idMap) {
@@ -193,7 +193,7 @@ function resolveBasedOnConfig(data, vertex, argument, environment, idMap, resolv
193
193
  full = false;
194
194
  }
195
195
  }
196
- return (0, resolve_by_name_1.resolve)(argument, { environment, graph: data.dataflow.graph, full });
196
+ return (0, resolve_by_name_1.resolveIdToValue)(argument, { environment, graph: data.dataflow.graph, full });
197
197
  }
198
198
  function unwrapRValue(value) {
199
199
  if (value === undefined) {
@@ -18,7 +18,7 @@ var DependencyInfoLinkConstraint;
18
18
  // these lists are originally based on https://github.com/duncantl/CodeDepends/blob/7fd96dfee16b252e5f642c77a7ababf48e9326f8/R/codeTypes.R
19
19
  exports.LibraryFunctions = [
20
20
  { name: 'library', argIdx: 0, argName: 'package', resolveValue: 'library' },
21
- { name: 'require', argIdx: 0, argName: 'package', resolveValue: true },
21
+ { name: 'require', argIdx: 0, argName: 'package', resolveValue: 'library' },
22
22
  { name: 'loadNamespace', argIdx: 0, argName: 'package', resolveValue: true },
23
23
  { name: 'attachNamespace', argIdx: 0, argName: 'ns', resolveValue: true },
24
24
  { name: 'attach', argIdx: 0, argName: 'what', resolveValue: true },
@@ -18,9 +18,9 @@ function executeResolveValueQuery({ dataflow: { graph }, ast }, queries) {
18
18
  }
19
19
  const values = query.criteria
20
20
  .map(criteria => (0, parse_1.slicingCriterionToId)(criteria, ast.idMap))
21
- .flatMap(ident => (0, resolve_by_name_1.resolve)(ident, { graph, full: true, idMap: ast.idMap }));
21
+ .flatMap(ident => (0, resolve_by_name_1.resolveIdToValue)(ident, { graph, full: true, idMap: ast.idMap }) ?? []);
22
22
  results[key] = {
23
- values: [...new Set(values)]
23
+ values: values ? [...new Set(values)] : []
24
24
  };
25
25
  }
26
26
  return {
@@ -10,7 +10,12 @@ export declare class TreeSitterExecutor implements SyncParser<Parser.Tree> {
10
10
  readonly name = "tree-sitter";
11
11
  readonly parser: Parser;
12
12
  private static language;
13
- static initTreeSitter(): Promise<void>;
13
+ /**
14
+ * Initializes the underlying tree-sitter parser. This only needs to be called once globally.
15
+ * @param overrideWasmPath - The path to the tree-sitter-r wasm file, which takes precedence over the config and default paths if set.
16
+ * @param overrideTreeSitterWasmPath - The path to the tree-sitter wasm file, which takes precedence over the config and default paths if set.
17
+ */
18
+ static initTreeSitter(overrideWasmPath?: string, overrideTreeSitterWasmPath?: string): Promise<void>;
14
19
  constructor();
15
20
  rVersion(): Promise<string | 'unknown' | 'none'>;
16
21
  treeSitterVersion(): number;
@@ -7,8 +7,10 @@ exports.TreeSitterExecutor = exports.DEFAULT_TREE_SITTER_WASM_PATH = exports.DEF
7
7
  const web_tree_sitter_1 = __importDefault(require("web-tree-sitter"));
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const config_1 = require("../../../config");
10
+ const log_1 = require("../../../util/log");
10
11
  exports.DEFAULT_TREE_SITTER_R_WASM_PATH = `${__dirname}/tree-sitter-r.wasm`;
11
12
  exports.DEFAULT_TREE_SITTER_WASM_PATH = `${__dirname}/tree-sitter.wasm`;
13
+ const wasmLog = log_1.log.getSubLogger({ name: 'tree-sitter-wasm' });
12
14
  /**
13
15
  * Synchronous and (way) faster alternative to the {@link RShell} using tree-sitter.
14
16
  */
@@ -16,9 +18,13 @@ class TreeSitterExecutor {
16
18
  name = 'tree-sitter';
17
19
  parser;
18
20
  static language;
19
- static async initTreeSitter() {
20
- const config = (0, config_1.getEngineConfig)('tree-sitter');
21
- const treeSitterWasmPath = config?.treeSitterWasmPath ?? exports.DEFAULT_TREE_SITTER_WASM_PATH;
21
+ /**
22
+ * Initializes the underlying tree-sitter parser. This only needs to be called once globally.
23
+ * @param overrideWasmPath - The path to the tree-sitter-r wasm file, which takes precedence over the config and default paths if set.
24
+ * @param overrideTreeSitterWasmPath - The path to the tree-sitter wasm file, which takes precedence over the config and default paths if set.
25
+ */
26
+ static async initTreeSitter(overrideWasmPath, overrideTreeSitterWasmPath) {
27
+ const treeSitterWasmPath = overrideTreeSitterWasmPath ?? (0, config_1.getEngineConfig)('tree-sitter')?.treeSitterWasmPath ?? exports.DEFAULT_TREE_SITTER_WASM_PATH;
22
28
  // noinspection JSUnusedGlobalSymbols - this is used by emscripten, see https://emscripten.org/docs/api_reference/module.html
23
29
  await web_tree_sitter_1.default.init({
24
30
  locateFile: (path, prefix) => {
@@ -27,9 +33,12 @@ class TreeSitterExecutor {
27
33
  return treeSitterWasmPath;
28
34
  }
29
35
  return prefix + path;
30
- }
36
+ },
37
+ onAbort: (s) => wasmLog.error(`Tree-sitter wasm aborted: ${s}`),
38
+ print: (s) => wasmLog.debug(s),
39
+ printErr: (s) => wasmLog.error(s)
31
40
  });
32
- const wasmPath = config?.wasmPath ?? exports.DEFAULT_TREE_SITTER_R_WASM_PATH;
41
+ const wasmPath = overrideWasmPath ?? (0, config_1.getEngineConfig)('tree-sitter')?.wasmPath ?? exports.DEFAULT_TREE_SITTER_R_WASM_PATH;
33
42
  TreeSitterExecutor.language = await web_tree_sitter_1.default.Language.load(wasmPath);
34
43
  }
35
44
  constructor() {
package/util/version.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.flowrVersion = flowrVersion;
4
4
  const semver_1 = require("semver");
5
5
  // this is automatically replaced with the current version by release-it
6
- const version = '2.2.6';
6
+ const version = '2.2.7';
7
7
  function flowrVersion() {
8
8
  return new semver_1.SemVer(version);
9
9
  }