@eagleoutice/flowr 2.0.1 → 2.0.2

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 (65) hide show
  1. package/benchmark/slicer.js +30 -5
  2. package/benchmark/stats/print.js +28 -9
  3. package/benchmark/stats/stats.d.ts +5 -0
  4. package/benchmark/summarizer/data.d.ts +10 -1
  5. package/benchmark/summarizer/first-phase/process.d.ts +1 -1
  6. package/benchmark/summarizer/first-phase/process.js +83 -30
  7. package/benchmark/summarizer/second-phase/process.js +20 -4
  8. package/cli/repl/commands/commands.js +19 -1
  9. package/cli/slicer-app.js +1 -1
  10. package/dataflow/environments/append.js +1 -2
  11. package/dataflow/environments/built-in.js +2 -1
  12. package/dataflow/environments/clone.js +1 -1
  13. package/dataflow/environments/diff.d.ts +1 -1
  14. package/dataflow/environments/diff.js +16 -18
  15. package/dataflow/environments/environment.d.ts +4 -7
  16. package/dataflow/environments/environment.js +5 -8
  17. package/dataflow/environments/identifier.d.ts +2 -1
  18. package/dataflow/environments/overwrite.js +1 -2
  19. package/dataflow/environments/scoping.js +1 -1
  20. package/dataflow/graph/diff.js +11 -6
  21. package/dataflow/graph/graph.d.ts +6 -2
  22. package/dataflow/graph/graph.js +13 -7
  23. package/dataflow/graph/vertex.d.ts +2 -1
  24. package/dataflow/info.d.ts +10 -1
  25. package/dataflow/info.js +54 -2
  26. package/dataflow/internal/linker.d.ts +1 -1
  27. package/dataflow/internal/linker.js +1 -2
  28. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +5 -5
  29. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +1 -1
  30. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +21 -25
  31. package/dataflow/internal/process/functions/call/built-in/built-in-get.js +6 -1
  32. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +10 -8
  33. package/dataflow/internal/process/functions/call/built-in/built-in-logical-bin-op.d.ts +1 -0
  34. package/dataflow/internal/process/functions/call/built-in/built-in-logical-bin-op.js +1 -2
  35. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +1 -1
  36. package/dataflow/internal/process/functions/call/default-call-handling.js +1 -1
  37. package/dataflow/internal/process/functions/call/unnamed-call-handling.js +1 -1
  38. package/dataflow/processor.d.ts +2 -3
  39. package/package.json +4 -2
  40. package/r-bridge/data/data.d.ts +1 -1
  41. package/r-bridge/data/data.js +1 -1
  42. package/r-bridge/lang-4.x/ast/model/nodes/r-function-call.d.ts +2 -2
  43. package/r-bridge/lang-4.x/ast/model/operators.js +1 -1
  44. package/r-bridge/lang-4.x/ast/model/processing/decorate.js +1 -1
  45. package/r-bridge/lang-4.x/ast/model/processing/stateful-fold.js +1 -1
  46. package/r-bridge/lang-4.x/ast/model/processing/visitor.js +2 -2
  47. package/r-bridge/lang-4.x/ast/parser/xml/internal/functions/normalize-call.js +2 -2
  48. package/r-bridge/lang-4.x/ast/parser/xml/internal/operators/normalize-binary.js +1 -1
  49. package/r-bridge/retriever.d.ts +1 -1
  50. package/r-bridge/retriever.js +3 -2
  51. package/r-bridge/shell.js +2 -1
  52. package/reconstruct/reconstruct.d.ts +3 -3
  53. package/reconstruct/reconstruct.js +40 -41
  54. package/slicing/criterion/filters/all-variables.js +1 -1
  55. package/slicing/static/static-slicer.js +2 -2
  56. package/statistics/features/common-syntax-probability.js +1 -1
  57. package/statistics/features/supported/control-flow/control-flow.js +1 -1
  58. package/statistics/features/supported/defined-functions/defined-functions.js +1 -1
  59. package/statistics/features/supported/loops/loops.js +1 -1
  60. package/statistics/features/supported/used-functions/used-functions.js +1 -1
  61. package/util/assert.d.ts +1 -1
  62. package/util/mermaid/ast.js +4 -0
  63. package/util/mermaid/dfg.js +15 -5
  64. package/util/mermaid/mermaid.js +21 -1
  65. package/util/version.js +1 -1
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.diffEnvironmentInformation = exports.diffEnvironment = exports.diffIdentifierReferences = void 0;
4
4
  const diff_1 = require("../../util/diff");
5
5
  const json_1 = require("../../util/json");
6
- const arrays_1 = require("../../util/arrays");
6
+ const info_1 = require("../info");
7
7
  function diffIdentifierReferences(a, b, info) {
8
8
  if (a === undefined || b === undefined) {
9
9
  if (a !== b) {
@@ -17,9 +17,7 @@ function diffIdentifierReferences(a, b, info) {
17
17
  if (a.nodeId !== b.nodeId) {
18
18
  info.report.addComment(`${info.position}Different nodeIds: ${info.leftname}: ${a.nodeId} vs. ${info.rightname}: ${b.nodeId}`);
19
19
  }
20
- if (!(0, arrays_1.arrayEqual)(a.controlDependencies, b.controlDependencies)) {
21
- info.report.addComment(`${info.position}Different control dependency: ${info.leftname}: ${JSON.stringify(a.controlDependencies)} vs. ${info.rightname}: ${JSON.stringify(b.controlDependencies)}`);
22
- }
20
+ (0, info_1.diffControlDependencies)(a.controlDependencies, b.controlDependencies, info);
23
21
  }
24
22
  exports.diffIdentifierReferences = diffIdentifierReferences;
25
23
  function diffMemory(a, b, info) {
@@ -41,9 +39,7 @@ function diffMemory(a, b, info) {
41
39
  if (aVal.nodeId !== bVal.nodeId) {
42
40
  info.report.addComment(`${info.position}Different ids for ${key}. ${info.leftname}: ${aVal.nodeId} vs. ${info.rightname}: ${bVal.nodeId}`);
43
41
  }
44
- if (!(0, arrays_1.arrayEqual)(aVal.controlDependencies, bVal.controlDependencies)) {
45
- info.report.addComment(`${info.position}Different controlDependency for ${key} (${aVal.nodeId}). ${info.leftname}: ${JSON.stringify(aVal.controlDependencies)} vs. ${info.rightname}: ${JSON.stringify(bVal.controlDependencies)}`);
46
- }
42
+ (0, info_1.diffControlDependencies)(aVal.controlDependencies, bVal.controlDependencies, { ...info, position: `${info.position} For ${key}. ` });
47
43
  if (aVal.definedAt !== bVal.definedAt) {
48
44
  info.report.addComment(`${info.position}Different definition ids (definedAt) for ${key} (${aVal.nodeId}). ${info.leftname}: ${aVal.definedAt} vs. ${info.rightname}: ${bVal.definedAt}`);
49
45
  }
@@ -53,33 +49,35 @@ function diffMemory(a, b, info) {
53
49
  }
54
50
  }
55
51
  }
56
- function diffEnvironment(a, b, info) {
52
+ function diffEnvironment(a, b, info, depth) {
57
53
  if (a === undefined || b === undefined) {
58
54
  if (a !== b) {
59
- info.report.addComment(`${info.position}Different environments. ${info.leftname}: ${a !== undefined ? 'present' : 'undefined'} vs. ${info.rightname}: ${b !== undefined ? 'present' : 'undefined'}`);
55
+ info.report.addComment(`${info.position}[at level: ${depth}] Different environments. ${info.leftname}: ${a !== undefined ? 'present' : 'undefined'} vs. ${info.rightname}: ${b !== undefined ? 'present' : 'undefined'}`);
60
56
  }
61
57
  return;
62
58
  }
63
- if (a.name !== b.name) {
64
- info.report.addComment(`${info.position}Different environment names. ${info.leftname}: ${a.name} vs. ${info.rightname}: ${b.name}`);
65
- }
66
59
  if (a.memory.size !== b.memory.size) {
67
- info.report.addComment(`${info.position}Different environment sizes. ${info.leftname}: ${a.memory.size} vs. ${info.rightname}: ${b.memory.size}`);
60
+ info.report.addComment(`${info.position}[at level: ${depth}] Different number of definitions in environment. ${info.leftname}: ${a.memory.size} vs. ${info.rightname}: ${b.memory.size}`);
68
61
  (0, diff_1.setDifference)(new Set([...a.memory.keys()]), new Set([...b.memory.keys()]), {
69
62
  ...info,
70
- position: `${info.position}Key comparison. `
63
+ position: `${info.position}[at level: ${depth}] Key comparison. `
71
64
  });
72
65
  }
73
- diffMemory(a, b, info);
74
- diffEnvironment(a.parent, b.parent, { ...info, position: `${info.position}Parents of ${a.id} & ${b.id}. ` });
66
+ diffMemory(a, b, { ...info, position: `${info.position}[at level: ${depth}] ` });
67
+ diffEnvironment(a.parent, b.parent, { ...info, position: `${info.position}Parents of ${a.id} & ${b.id}. ` }, depth--);
75
68
  }
76
69
  exports.diffEnvironment = diffEnvironment;
77
70
  function diffEnvironmentInformation(a, b, info) {
78
71
  if (a === undefined || b === undefined) {
79
- info.report.addComment(`${info.position}Different environments: ${JSON.stringify(a, json_1.jsonReplacer)} vs. ${JSON.stringify(b, json_1.jsonReplacer)}`);
72
+ if (a !== b) {
73
+ info.report.addComment(`${info.position}Different environments: ${JSON.stringify(a, json_1.jsonReplacer)} vs. ${JSON.stringify(b, json_1.jsonReplacer)}`);
74
+ }
80
75
  return;
81
76
  }
82
- diffEnvironment(a.current, b.current, info);
77
+ if (a.level !== b.level) {
78
+ info.report.addComment(`${info.position}Different environment levels: ${info.leftname}: ${a.level} vs. ${info.rightname}: ${b.level}. Using max to report level for further errors.`);
79
+ }
80
+ diffEnvironment(a.current, b.current, info, Math.max(a.level, b.level));
83
81
  }
84
82
  exports.diffEnvironmentInformation = diffEnvironmentInformation;
85
83
  //# sourceMappingURL=diff.js.map
@@ -6,13 +6,12 @@
6
6
  */
7
7
  import type { Identifier, IdentifierDefinition, IdentifierReference } from './identifier';
8
8
  import type { DataflowGraph } from '../graph/graph';
9
- import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
10
- export declare function makeReferenceMaybe(ref: IdentifierReference, graph: DataflowGraph, environments: REnvironmentInformation, includeDefs: boolean, defaultCd?: NodeId | undefined): IdentifierReference;
11
- export declare function makeAllMaybe(references: readonly IdentifierReference[] | undefined, graph: DataflowGraph, environments: REnvironmentInformation, includeDefs: boolean, defaultCd?: NodeId | undefined): IdentifierReference[];
9
+ import type { ControlDependency } from '../info';
10
+ export declare function makeReferenceMaybe(ref: IdentifierReference, graph: DataflowGraph, environments: REnvironmentInformation, includeDefs: boolean, defaultCd?: ControlDependency | undefined): IdentifierReference;
11
+ export declare function makeAllMaybe(references: readonly IdentifierReference[] | undefined, graph: DataflowGraph, environments: REnvironmentInformation, includeDefs: boolean, defaultCd?: ControlDependency | undefined): IdentifierReference[];
12
12
  export interface IEnvironment {
13
13
  /** unique and internally generated identifier -- will not be used for comparison but assists debugging for tracking identities */
14
14
  readonly id: string;
15
- readonly name: string;
16
15
  /** Lexical parent of the environment, if any (can be manipulated by R code) */
17
16
  parent: IEnvironment;
18
17
  /**
@@ -21,11 +20,10 @@ export interface IEnvironment {
21
20
  memory: Map<Identifier, IdentifierDefinition[]>;
22
21
  }
23
22
  export declare class Environment implements IEnvironment {
24
- readonly name: string;
25
23
  readonly id: string;
26
24
  parent: IEnvironment;
27
25
  memory: Map<Identifier, IdentifierDefinition[]>;
28
- constructor(name: string, parent: IEnvironment);
26
+ constructor(parent: IEnvironment);
29
27
  }
30
28
  /**
31
29
  * First of all, yes, R stores its environments differently, potentially even with a different differentiation between
@@ -41,5 +39,4 @@ export interface REnvironmentInformation {
41
39
  readonly level: number;
42
40
  }
43
41
  export declare const BuiltInEnvironment: Environment;
44
- export declare const GLOBAL_ENV_NAME = "global";
45
42
  export declare function initializeCleanEnvironments(): REnvironmentInformation;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.initializeCleanEnvironments = exports.GLOBAL_ENV_NAME = exports.BuiltInEnvironment = exports.Environment = exports.makeAllMaybe = exports.makeReferenceMaybe = void 0;
3
+ exports.initializeCleanEnvironments = exports.BuiltInEnvironment = exports.Environment = exports.makeAllMaybe = exports.makeReferenceMaybe = void 0;
4
4
  const built_in_1 = require("./built-in");
5
5
  const resolve_by_name_1 = require("./resolve-by-name");
6
6
  function makeReferenceMaybe(ref, graph, environments, includeDefs, defaultCd = undefined) {
@@ -9,7 +9,7 @@ function makeReferenceMaybe(ref, graph, environments, includeDefs, defaultCd = u
9
9
  const definitions = ref.name ? (0, resolve_by_name_1.resolveByName)(ref.name, environments) : undefined;
10
10
  for (const definition of definitions ?? []) {
11
11
  if (definition.kind !== 'built-in-function' && definition.kind !== 'built-in-value') {
12
- if (definition.controlDependencies && defaultCd && !definition.controlDependencies.includes(defaultCd)) {
12
+ if (definition.controlDependencies && defaultCd && !definition.controlDependencies.find(c => c.id === defaultCd.id)) {
13
13
  definition.controlDependencies.push(defaultCd);
14
14
  }
15
15
  else {
@@ -38,24 +38,21 @@ function makeAllMaybe(references, graph, environments, includeDefs, defaultCd =
38
38
  exports.makeAllMaybe = makeAllMaybe;
39
39
  let environmentIdCounter = 0;
40
40
  class Environment {
41
- name;
42
41
  id = `${environmentIdCounter++}`;
43
42
  parent;
44
43
  memory;
45
- constructor(name, parent) {
46
- this.name = name;
44
+ constructor(parent) {
47
45
  this.parent = parent;
48
46
  this.memory = new Map();
49
47
  }
50
48
  }
51
49
  exports.Environment = Environment;
52
50
  /* the built-in environment is the root of all environments */
53
- exports.BuiltInEnvironment = new Environment('built-in', undefined);
51
+ exports.BuiltInEnvironment = new Environment(undefined);
54
52
  exports.BuiltInEnvironment.memory = built_in_1.BuiltInMemory;
55
- exports.GLOBAL_ENV_NAME = 'global';
56
53
  function initializeCleanEnvironments() {
57
54
  return {
58
- current: new Environment(exports.GLOBAL_ENV_NAME, exports.BuiltInEnvironment),
55
+ current: new Environment(exports.BuiltInEnvironment),
59
56
  level: 0
60
57
  };
61
58
  }
@@ -1,5 +1,6 @@
1
1
  import type { BuiltInIdentifierConstant, BuiltInIdentifierDefinition } from './built-in';
2
2
  import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
3
+ import type { ControlDependency } from '../info';
3
4
  export type Identifier = string & {
4
5
  __brand?: 'identifier';
5
6
  };
@@ -26,6 +27,6 @@ export interface IdentifierReference {
26
27
  * If the reference is only effective if, e.g. an if-then-else condition is true, this references the root of the `if`.
27
28
  * As a hackey intermediate solution (until we have pointer-analysis), an empty array may indicate a `maybe` which is due to pointer access (e.g., in `a[x] <- 3`).
28
29
  */
29
- controlDependencies: NodeId[] | undefined;
30
+ controlDependencies: ControlDependency[] | undefined;
30
31
  }
31
32
  export {};
@@ -16,7 +16,6 @@ function anyIsMaybeOrEmpty(values) {
16
16
  }
17
17
  function overwriteIEnvironmentWith(base, next, includeParent = true) {
18
18
  (0, assert_1.guard)(base !== undefined && next !== undefined, 'can not overwrite environments with undefined');
19
- (0, assert_1.guard)(base.name === next.name, 'cannot overwrite environments with different names');
20
19
  const map = new Map(base.memory);
21
20
  for (const [key, values] of next.memory) {
22
21
  const hasMaybe = anyIsMaybeOrEmpty(values);
@@ -43,7 +42,7 @@ function overwriteIEnvironmentWith(base, next, includeParent = true) {
43
42
  else {
44
43
  parent = base.parent;
45
44
  }
46
- const out = new environment_1.Environment(base.name, parent);
45
+ const out = new environment_1.Environment(parent);
47
46
  out.memory = map;
48
47
  return out;
49
48
  }
@@ -6,7 +6,7 @@ const assert_1 = require("../../util/assert");
6
6
  /** Add a new local environment scope to the stack, returns the modified variant - sharing the original environments in the stack (no deep-clone) */
7
7
  function pushLocalEnvironment(base) {
8
8
  return {
9
- current: new environment_1.Environment('local', base.current),
9
+ current: new environment_1.Environment(base.current),
10
10
  level: base.level + 1
11
11
  };
12
12
  }
@@ -9,6 +9,7 @@ const edge_1 = require("./edge");
9
9
  const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id");
10
10
  const diff_2 = require("../environments/diff");
11
11
  const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
12
+ const info_1 = require("../info");
12
13
  class DataflowDifferenceReport {
13
14
  _comments = undefined;
14
15
  _problematic = undefined;
@@ -62,10 +63,18 @@ function diffOutgoingEdges(ctx) {
62
63
  ctx.report.addComment(`Detected different number of edges! ${ctx.leftname} has ${lEdges.size} (${JSON.stringify(lEdges, json_1.jsonReplacer)}). ${ctx.rightname} has ${rEdges.size} ${JSON.stringify(rEdges, json_1.jsonReplacer)}`);
63
64
  }
64
65
  for (const [id, edge] of lEdges) {
66
+ if (!ctx.left.hasVertex(id)) {
67
+ ctx.report.addComment(`The source ${id} of edges ${JSON.stringify(edge, json_1.jsonReplacer)} is not present in ${ctx.leftname}. This means that the graph contains an edge but not the corresponding vertex.`);
68
+ continue;
69
+ }
65
70
  diffEdges(ctx, id, edge, rEdges.get(id));
66
71
  }
67
72
  // just to make it both ways in case the length differs
68
73
  for (const [id, edge] of rEdges) {
74
+ if (!ctx.right.hasVertex(id)) {
75
+ ctx.report.addComment(`The source ${id} of edges ${JSON.stringify(edge, json_1.jsonReplacer)} is not present in ${ctx.rightname}. This means that the graph contains an edge but not the corresponding vertex.`);
76
+ continue;
77
+ }
69
78
  if (!lEdges.has(id)) {
70
79
  diffEdges(ctx, id, undefined, edge);
71
80
  }
@@ -137,9 +146,7 @@ function diffFunctionArguments(fn, a, b, ctx) {
137
146
  if (aArg.name !== bArg.name) {
138
147
  ctx.report.addComment(`${ctx.position}In argument #${i} (of ${ctx.leftname}, unnamed) the name differs: ${aArg.name} vs ${bArg.name}.`);
139
148
  }
140
- if (!(0, arrays_1.arrayEqual)(aArg.controlDependencies, bArg.controlDependencies)) {
141
- ctx.report.addComment(`${ctx.position}In argument #${i} (of ${ctx.leftname}, unnamed) the control dependency differs: ${JSON.stringify(aArg.controlDependencies)} vs ${JSON.stringify(bArg.controlDependencies)}.`, { tag: 'vertex', id: fn });
142
- }
149
+ (0, info_1.diffControlDependencies)(aArg.controlDependencies, bArg.controlDependencies, { ...ctx, position: `${ctx.position}In argument #${i} (of ${ctx.leftname}, unnamed) the control dependency differs: ${JSON.stringify(aArg.controlDependencies)} vs ${JSON.stringify(bArg.controlDependencies)}.` });
143
150
  }
144
151
  }
145
152
  }
@@ -173,9 +180,7 @@ function diffVertices(ctx) {
173
180
  });
174
181
  }
175
182
  }
176
- if (!(0, arrays_1.arrayEqual)(lInfo.controlDependencies, rInfo.controlDependencies)) {
177
- ctx.report.addComment(`Vertex ${id} differs in controlDependency. ${ctx.leftname}: ${JSON.stringify(lInfo.controlDependencies)} vs ${ctx.rightname}: ${JSON.stringify(rInfo.controlDependencies)}`, { tag: 'vertex', id });
178
- }
183
+ (0, info_1.diffControlDependencies)(lInfo.controlDependencies, rInfo.controlDependencies, { ...ctx, position: `Vertex ${id} differs in controlDependencies. ` });
179
184
  (0, diff_2.diffEnvironmentInformation)(lInfo.environment, rInfo.environment, { ...ctx, position: `${ctx.position}Vertex ${id} differs in environment. ` });
180
185
  if (lInfo.tag === "function-call" /* VertexType.FunctionCall */) {
181
186
  if (rInfo.tag !== "function-call" /* VertexType.FunctionCall */) {
@@ -46,7 +46,7 @@ type EdgeData<Edge extends DataflowGraphEdge> = Omit<Edge, 'from' | 'to' | 'type
46
46
  */
47
47
  export declare class DataflowGraph<Vertex extends DataflowGraphVertexInfo = DataflowGraphVertexInfo, Edge extends DataflowGraphEdge = DataflowGraphEdge> {
48
48
  private static DEFAULT_ENVIRONMENT;
49
- readonly idMap: AstIdMap | undefined;
49
+ private _idMap;
50
50
  constructor(idMap: AstIdMap | undefined);
51
51
  /** Contains the vertices of the root level graph (i.e., included those vertices from the complete graph, that are nested within function definitions) */
52
52
  private rootVertices;
@@ -76,6 +76,10 @@ export declare class DataflowGraph<Vertex extends DataflowGraphVertexInfo = Data
76
76
  getVertex(id: NodeId, includeDefinedFunctions?: boolean): Vertex | undefined;
77
77
  outgoingEdges(id: NodeId): OutgoingEdges | undefined;
78
78
  ingoingEdges(id: NodeId): IngoingEdges | undefined;
79
+ /** Retrieves the id-map to the normalized AST attached to the dataflow graph */
80
+ get idMap(): AstIdMap | undefined;
81
+ /** Allows setting the id-map explicitly (which should only be used when, e.g., you plan to compare two dataflow graphs on the same AST-basis) */
82
+ setIdMap(idMap: AstIdMap): void;
79
83
  /**
80
84
  * @param includeDefinedFunctions - If true this will iterate over function definitions as well and not just the toplevel
81
85
  * @returns the ids of all toplevel vertices in the graph together with their vertex information
@@ -95,7 +99,7 @@ export declare class DataflowGraph<Vertex extends DataflowGraphVertexInfo = Data
95
99
  * @param id - The id to check for
96
100
  * @param includeDefinedFunctions - If true this will check function definitions as well and not just the toplevel
97
101
  */
98
- hasVertex(id: NodeId, includeDefinedFunctions: boolean): boolean;
102
+ hasVertex(id: NodeId, includeDefinedFunctions?: boolean): boolean;
99
103
  /**
100
104
  * Returns true if the root level of the graph contains a node with the given id.
101
105
  */
@@ -40,10 +40,10 @@ function extractEdgeIds(from, to) {
40
40
  */
41
41
  class DataflowGraph {
42
42
  static DEFAULT_ENVIRONMENT = undefined;
43
- idMap;
43
+ _idMap;
44
44
  constructor(idMap) {
45
45
  DataflowGraph.DEFAULT_ENVIRONMENT ??= (0, environment_1.initializeCleanEnvironments)();
46
- this.idMap = idMap;
46
+ this._idMap = idMap;
47
47
  }
48
48
  /** Contains the vertices of the root level graph (i.e., included those vertices from the complete graph, that are nested within function definitions) */
49
49
  rootVertices = new Set();
@@ -89,6 +89,14 @@ class DataflowGraph {
89
89
  }
90
90
  return edges;
91
91
  }
92
+ /** Retrieves the id-map to the normalized AST attached to the dataflow graph */
93
+ get idMap() {
94
+ return this._idMap;
95
+ }
96
+ /** Allows setting the id-map explicitly (which should only be used when, e.g., you plan to compare two dataflow graphs on the same AST-basis) */
97
+ setIdMap(idMap) {
98
+ this._idMap = idMap;
99
+ }
92
100
  /**
93
101
  * @param includeDefinedFunctions - If true this will iterate over function definitions as well and not just the toplevel
94
102
  * @returns the ids of all toplevel vertices in the graph together with their vertex information
@@ -119,7 +127,7 @@ class DataflowGraph {
119
127
  * @param id - The id to check for
120
128
  * @param includeDefinedFunctions - If true this will check function definitions as well and not just the toplevel
121
129
  */
122
- hasVertex(id, includeDefinedFunctions) {
130
+ hasVertex(id, includeDefinedFunctions = true) {
123
131
  return includeDefinedFunctions ? this.vertexInformation.has(id) : this.rootVertices.has(id);
124
132
  }
125
133
  /**
@@ -146,8 +154,9 @@ class DataflowGraph {
146
154
  if (oldVertex !== undefined) {
147
155
  return this;
148
156
  }
157
+ const fallback = vertex.tag === "variable-definition" /* VertexType.VariableDefinition */ || vertex.tag === "use" /* VertexType.Use */ || vertex.tag === "value" /* VertexType.Value */ ? undefined : DataflowGraph.DEFAULT_ENVIRONMENT;
149
158
  // keep a clone of the original environment
150
- const environment = vertex.environment === undefined ? DataflowGraph.DEFAULT_ENVIRONMENT : (0, clone_1.cloneEnvironmentInformation)(vertex.environment);
159
+ const environment = vertex.environment === undefined ? fallback : (0, clone_1.cloneEnvironmentInformation)(vertex.environment);
151
160
  this.vertexInformation.set(vertex.id, {
152
161
  ...vertex,
153
162
  when: vertex.controlDependencies ?? 'always',
@@ -263,9 +272,6 @@ class DataflowGraph {
263
272
  const vertex = this.getVertex(reference.nodeId, true);
264
273
  (0, assert_1.guard)(vertex !== undefined, () => `node must be defined for ${JSON.stringify(reference)} to set reference`);
265
274
  if (vertex.tag === "function-definition" /* VertexType.FunctionDefinition */ || vertex.tag === "variable-definition" /* VertexType.VariableDefinition */) {
266
- (0, assert_1.guard)(vertex.controlDependencies !== undefined
267
- || reference.controlDependencies !== undefined
268
- || (0, arrays_1.arrayEqual)(vertex.controlDependencies, reference.controlDependencies), () => `node ${JSON.stringify(vertex)} must not be previously defined at position or have same scope for ${JSON.stringify(reference)}`);
269
275
  vertex.controlDependencies = reference.controlDependencies;
270
276
  }
271
277
  else {
@@ -2,6 +2,7 @@ import type { MergeableRecord } from '../../util/objects';
2
2
  import type { DataflowFunctionFlowInformation, FunctionArgument } from './graph';
3
3
  import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
4
4
  import type { REnvironmentInformation } from '../environments/environment';
5
+ import type { ControlDependency } from '../info';
5
6
  export type DataflowGraphVertices<Vertex extends DataflowGraphVertexInfo = DataflowGraphVertexInfo> = Map<NodeId, Vertex>;
6
7
  export declare const enum VertexType {
7
8
  Value = "value",
@@ -33,7 +34,7 @@ interface DataflowGraphVertexBase extends MergeableRecord {
33
34
  /**
34
35
  * See {@link IdentifierReference}
35
36
  */
36
- controlDependencies: NodeId[] | undefined;
37
+ controlDependencies: ControlDependency[] | undefined;
37
38
  }
38
39
  export interface DataflowGraphValue extends DataflowGraphVertexBase {
39
40
  readonly tag: VertexType.Value;
@@ -3,16 +3,22 @@ import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id';
3
3
  import type { IdentifierReference } from './environments/identifier';
4
4
  import type { REnvironmentInformation } from './environments/environment';
5
5
  import { DataflowGraph } from './graph/graph';
6
+ import type { GenericDifferenceInformation, WriteableDifferenceReport } from '../util/diff';
6
7
  export declare const enum ExitPointType {
7
8
  Default = 0,
8
9
  Return = 1,
9
10
  Break = 2,
10
11
  Next = 3
11
12
  }
13
+ export interface ControlDependency {
14
+ readonly id: NodeId;
15
+ /** when does this control dependency trigger (if the condition is true or false)? */
16
+ readonly when?: boolean;
17
+ }
12
18
  export interface ExitPoint {
13
19
  readonly type: ExitPointType;
14
20
  readonly nodeId: NodeId;
15
- readonly controlDependencies: NodeId[] | undefined;
21
+ readonly controlDependencies: ControlDependency[] | undefined;
16
22
  }
17
23
  export declare function addNonDefaultExitPoints(existing: ExitPoint[], add: readonly ExitPoint[]): void;
18
24
  /**
@@ -45,5 +51,8 @@ export interface DataflowInformation extends DataflowCfgInformation {
45
51
  graph: DataflowGraph;
46
52
  }
47
53
  export declare function initializeCleanDataflowInformation<T>(entryPoint: NodeId, data: Pick<DataflowProcessorInformation<T>, 'environment' | 'completeAst'>): DataflowInformation;
54
+ export declare function happensInEveryBranch(controlDependencies: ControlDependency[] | undefined): boolean;
48
55
  export declare function alwaysExits(data: DataflowInformation): boolean;
49
56
  export declare function filterOutLoopExitPoints(exitPoints: readonly ExitPoint[]): readonly ExitPoint[];
57
+ export declare function diffControlDependency<Report extends WriteableDifferenceReport>(a: ControlDependency | undefined, b: ControlDependency | undefined, info: GenericDifferenceInformation<Report>): void;
58
+ export declare function diffControlDependencies<Report extends WriteableDifferenceReport>(a: ControlDependency[] | undefined, b: ControlDependency[] | undefined, info: GenericDifferenceInformation<Report>): void;
package/dataflow/info.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.filterOutLoopExitPoints = exports.alwaysExits = exports.initializeCleanDataflowInformation = exports.addNonDefaultExitPoints = void 0;
3
+ exports.diffControlDependencies = exports.diffControlDependency = exports.filterOutLoopExitPoints = exports.alwaysExits = exports.happensInEveryBranch = exports.initializeCleanDataflowInformation = exports.addNonDefaultExitPoints = void 0;
4
4
  const graph_1 = require("./graph/graph");
5
5
  function addNonDefaultExitPoints(existing, add) {
6
6
  existing.push(...add.filter(({ type }) => type !== 0 /* ExitPointType.Default */));
@@ -18,12 +18,64 @@ function initializeCleanDataflowInformation(entryPoint, data) {
18
18
  };
19
19
  }
20
20
  exports.initializeCleanDataflowInformation = initializeCleanDataflowInformation;
21
+ function happensInEveryBranch(controlDependencies) {
22
+ if (controlDependencies === undefined) {
23
+ /* the cds are unconstrained */
24
+ return true;
25
+ }
26
+ else if (controlDependencies.length === 0) {
27
+ /* this happens only when we have no idea and require more analysis */
28
+ return false;
29
+ }
30
+ const trues = [];
31
+ const falseSet = new Set();
32
+ for (const { id, when } of controlDependencies) {
33
+ if (when) {
34
+ trues.push(id);
35
+ }
36
+ else {
37
+ falseSet.add(id);
38
+ }
39
+ }
40
+ return trues.every(id => falseSet.has(id));
41
+ }
42
+ exports.happensInEveryBranch = happensInEveryBranch;
21
43
  function alwaysExits(data) {
22
- return data.exitPoints?.some(e => e.type !== 0 /* ExitPointType.Default */ && e.controlDependencies === undefined) ?? false;
44
+ return data.exitPoints?.some(e => e.type !== 0 /* ExitPointType.Default */ && happensInEveryBranch(e.controlDependencies)) ?? false;
23
45
  }
24
46
  exports.alwaysExits = alwaysExits;
25
47
  function filterOutLoopExitPoints(exitPoints) {
26
48
  return exitPoints.filter(({ type }) => type === 1 /* ExitPointType.Return */ || type === 0 /* ExitPointType.Default */);
27
49
  }
28
50
  exports.filterOutLoopExitPoints = filterOutLoopExitPoints;
51
+ function diffControlDependency(a, b, info) {
52
+ if (a === undefined || b === undefined) {
53
+ if (a !== b) {
54
+ info.report.addComment(`${info.position}Different control dependencies. ${info.leftname}: ${JSON.stringify(a)} vs. ${info.rightname}: ${JSON.stringify(b)}`);
55
+ }
56
+ return;
57
+ }
58
+ if (a.id !== b.id) {
59
+ info.report.addComment(`${info.position}Different control dependency ids. ${info.leftname}: ${a.id} vs. ${info.rightname}: ${b.id}`);
60
+ }
61
+ if (a.when !== b.when) {
62
+ info.report.addComment(`${info.position}Different control dependency when. ${info.leftname}: ${a.when} vs. ${info.rightname}: ${b.when}`);
63
+ }
64
+ }
65
+ exports.diffControlDependency = diffControlDependency;
66
+ function diffControlDependencies(a, b, info) {
67
+ if (a === undefined || b === undefined) {
68
+ if (a !== b) {
69
+ info.report.addComment(`${info.position}Different control dependencies: ${JSON.stringify(a)} vs. ${JSON.stringify(b)}`);
70
+ }
71
+ return;
72
+ }
73
+ if (a.length !== b.length) {
74
+ info.report.addComment(`${info.position}Different control dependency lengths: ${a.length} vs. ${b.length}`);
75
+ }
76
+ for (let i = 0; i < a.length; ++i) {
77
+ diffControlDependency(a[i], b[i], { ...info, position: `${info.position}Control dependency at index: ${i}: ` });
78
+ }
79
+ }
80
+ exports.diffControlDependencies = diffControlDependencies;
29
81
  //# sourceMappingURL=info.js.map
@@ -17,7 +17,7 @@ export declare function linkFunctionCalls(graph: DataflowGraph, idMap: AstIdMap,
17
17
  functionCall: NodeId;
18
18
  called: readonly DataflowGraphVertexInfo[];
19
19
  }[];
20
- export declare function getAllLinkedFunctionDefinitions(functionDefinitionReadIds: Set<NodeId>, dataflowGraph: DataflowGraph): Map<NodeId, DataflowGraphVertexInfo>;
20
+ export declare function getAllLinkedFunctionDefinitions(functionDefinitionReadIds: ReadonlySet<NodeId>, dataflowGraph: DataflowGraph): Map<NodeId, DataflowGraphVertexInfo>;
21
21
  /**
22
22
  * This method links a set of read variables to definitions in an environment.
23
23
  *
@@ -136,9 +136,8 @@ function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGrap
136
136
  const result = new Map();
137
137
  while (potential.length > 0) {
138
138
  const currentId = potential.pop();
139
+ // do not traverse builtins
139
140
  if (currentId === built_in_1.BuiltIn) {
140
- // do not traverse builtins
141
- static_slicer_1.slicerLogger.trace('skipping builtin function definition during collection');
142
141
  continue;
143
142
  }
144
143
  const currentInfo = dataflowGraph.get(currentId, true);
@@ -39,7 +39,7 @@ args, rootId, data, config) {
39
39
  }
40
40
  const effectiveArgs = getEffectiveOrder(config, args);
41
41
  const { target, source } = extractSourceAndTarget(effectiveArgs, name);
42
- const { type, flavor } = target;
42
+ const { type, named } = target;
43
43
  if (type === "RSymbol" /* RType.Symbol */) {
44
44
  const res = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, reverseOrder: !config.swapSourceAndTarget });
45
45
  return processAssignmentToSymbol({
@@ -53,7 +53,7 @@ args, rootId, data, config) {
53
53
  information: res.information,
54
54
  });
55
55
  }
56
- else if (config.canBeReplacement && type === "RFunctionCall" /* RType.FunctionCall */ && flavor === 'named') {
56
+ else if (config.canBeReplacement && type === "RFunctionCall" /* RType.FunctionCall */ && named) {
57
57
  /* as replacement functions take precedence over the lhs fn-call (i.e., `names(x) <- ...` is independent from the definition of `names`), we do not have to process the call */
58
58
  logger_1.dataflowLogger.debug(`Assignment ${name.content} has a function call as target => replacement function ${target.lexeme}`);
59
59
  const replacement = toReplacementSymbol(target, target.functionName.content, config.superAssignment ?? false);
@@ -132,7 +132,7 @@ function processAssignmentToSymbol({ name, source, args: [targetArg, sourceArg],
132
132
  }
133
133
  // we drop the first arg which we use to pass along arguments :D
134
134
  const readFromSourceWritten = sourceArg.out.slice(1);
135
- const readTargets = [{ nodeId: name.info.id, name: name.content, controlDependencies: data.controlDependencies }, ...sourceArg.unknownReferences, ...sourceArg.in, ...targetArg.in.filter(i => i.nodeId !== target.info.id), ...readFromSourceWritten];
135
+ const readTargets = [{ nodeId: rootId, name: name.content, controlDependencies: data.controlDependencies }, ...sourceArg.unknownReferences, ...sourceArg.in, ...targetArg.in.filter(i => i.nodeId !== target.info.id), ...readFromSourceWritten];
136
136
  const writeTargets = [...writeNodes, ...writeNodes, ...readFromSourceWritten];
137
137
  information.environment = (0, overwrite_1.overwriteEnvironment)(targetArg.environment, sourceArg.environment);
138
138
  // install assigned variables in environment
@@ -152,14 +152,14 @@ function processAssignmentToSymbol({ name, source, args: [targetArg, sourceArg],
152
152
  }
153
153
  }
154
154
  }
155
- information.graph.addEdge(name.info.id, targetArg.entryPoint, { type: 8 /* EdgeType.Returns */ });
155
+ information.graph.addEdge(rootId, targetArg.entryPoint, { type: 8 /* EdgeType.Returns */ });
156
156
  if (quoteSource) {
157
157
  information.graph.addEdge(rootId, source.info.id, { type: 256 /* EdgeType.NonStandardEvaluation */ });
158
158
  }
159
159
  return {
160
160
  ...information,
161
161
  unknownReferences: [],
162
- entryPoint: name.info.id,
162
+ entryPoint: rootId,
163
163
  in: readTargets,
164
164
  out: writeTargets
165
165
  };
@@ -29,7 +29,7 @@ function processForLoop(name, args, rootId, data) {
29
29
  const variable = (0, processor_1.processDataflowFor)(variableArg, data);
30
30
  // this should not be able to exit always!
31
31
  const originalDependency = data.controlDependencies;
32
- data = { ...data, controlDependencies: [...data.controlDependencies ?? [], name.info.id] };
32
+ data = { ...data, controlDependencies: [...data.controlDependencies ?? [], { id: name.info.id, when: true }] };
33
33
  let headEnvironments = (0, overwrite_1.overwriteEnvironment)(vector.environment, variable.environment);
34
34
  const headGraph = variable.graph.mergeWith(vector.graph);
35
35
  const writtenVariable = [...variable.unknownReferences, ...variable.in];
@@ -13,6 +13,7 @@ const overwrite_1 = require("../../../../../environments/overwrite");
13
13
  const scoping_1 = require("../../../../../environments/scoping");
14
14
  const environment_1 = require("../../../../../environments/environment");
15
15
  const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
16
+ const log_1 = require("../../../../../../util/log");
16
17
  function processFunctionDefinition(name, args, rootId, data) {
17
18
  if (args.length < 1) {
18
19
  logger_1.dataflowLogger.warn(`Function Definition ${name.content} does not have an argument, skipping`);
@@ -53,7 +54,7 @@ function processFunctionDefinition(name, args, rootId, data) {
53
54
  tag: "use" /* VertexType.Use */,
54
55
  id: read.nodeId,
55
56
  environment: undefined,
56
- controlDependencies: []
57
+ controlDependencies: undefined
57
58
  });
58
59
  }
59
60
  }
@@ -65,8 +66,8 @@ function processFunctionDefinition(name, args, rootId, data) {
65
66
  graph: new Set(subgraph.rootIds()),
66
67
  environment: outEnvironment
67
68
  };
69
+ updateNestedFunctionClosures(subgraph, outEnvironment, name);
68
70
  const exitPoints = body.exitPoints;
69
- updateNestedFunctionClosures(exitPoints, subgraph, outEnvironment, name);
70
71
  const graph = new graph_1.DataflowGraph(data.completeAst.idMap).mergeWith(subgraph, false);
71
72
  graph.addVertex({
72
73
  tag: "function-definition" /* VertexType.FunctionDefinition */,
@@ -88,33 +89,28 @@ function processFunctionDefinition(name, args, rootId, data) {
88
89
  };
89
90
  }
90
91
  exports.processFunctionDefinition = processFunctionDefinition;
91
- function updateNestedFunctionClosures(exitPoints, subgraph, outEnvironment, name) {
92
- // track *all* function definitions - including those nested within the current graph
92
+ function updateNestedFunctionClosures(subgraph, outEnvironment, name) {
93
+ // track *all* function definitions - including those nested within the current graph,
93
94
  // try to resolve their 'in' by only using the lowest scope which will be popped after this definition
94
- for (const [id, info] of subgraph.vertices(true)) {
95
- if (info.tag !== "function-definition" /* VertexType.FunctionDefinition */) {
95
+ for (const [id, { subflow, tag }] of subgraph.vertices(true)) {
96
+ if (tag !== "function-definition" /* VertexType.FunctionDefinition */) {
96
97
  continue;
97
98
  }
98
- const ingoingRefs = info.subflow.in;
99
- const remainingIn = new Set();
99
+ const ingoingRefs = subflow.in;
100
+ const remainingIn = [];
100
101
  for (const ingoing of ingoingRefs) {
101
- for (const { nodeId } of exitPoints) {
102
- const node = subgraph.getVertex(nodeId, true);
103
- const env = (0, environment_1.initializeCleanEnvironments)();
104
- env.current.memory = node === undefined ? outEnvironment.current.memory : (node.environment?.current.memory ?? outEnvironment.current.memory);
105
- const resolved = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, env) : undefined;
106
- if (resolved === undefined) {
107
- remainingIn.add(ingoing);
108
- continue;
109
- }
110
- logger_1.dataflowLogger.trace(`Found ${resolved.length} references to open ref ${id} in closure of function definition ${name.info.id}`);
111
- for (const ref of resolved) {
112
- subgraph.addEdge(ingoing, ref, { type: 1 /* EdgeType.Reads */ });
113
- }
102
+ const resolved = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, outEnvironment) : undefined;
103
+ if (resolved === undefined) {
104
+ remainingIn.push(ingoing);
105
+ continue;
106
+ }
107
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${id} in closure of function definition ${name.info.id}`);
108
+ for (const ref of resolved) {
109
+ subgraph.addEdge(ingoing, ref, { type: 1 /* EdgeType.Reads */ });
114
110
  }
115
111
  }
116
- logger_1.dataflowLogger.trace(`Keeping ${remainingIn.size} (unique) references to open ref ${id} in closure of function definition ${name.info.id}`);
117
- info.subflow.in = [...remainingIn];
112
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Keeping ${remainingIn.length} references to open ref ${id} in closure of function definition ${name.info.id}`);
113
+ subflow.in = remainingIn;
118
114
  }
119
115
  }
120
116
  function prepareFunctionEnvironment(data) {
@@ -127,11 +123,11 @@ function prepareFunctionEnvironment(data) {
127
123
  /**
128
124
  * Within something like `f <- function(a=b, m=3) { b <- 1; a; b <- 5; a + 1 }`
129
125
  * `a` will be defined by `b` and `b`will be a promise object bound by the first definition of b it can find.
130
- * This means, that this function returns `2` due to the first `b <- 1` definition.
126
+ * This means that this function returns `2` due to the first `b <- 1` definition.
131
127
  * If the code is `f <- function(a=b, m=3) { if(m > 3) { b <- 1; }; a; b <- 5; a + 1 }`, we need a link to `b <- 1` and `b <- 6`
132
128
  * as `b` can be defined by either one of them.
133
129
  * <p>
134
- * <b>Currently we may be unable to narrow down every definition within the body as we have not implemented ways to track what covers a first definitions</b>
130
+ * <b>Currently we may be unable to narrow down every definition within the body as we have not implemented ways to track what covers the first definitions precisely</b>
135
131
  */
136
132
  function findPromiseLinkagesForParameters(parameters, readInParameters, parameterEnvs, body) {
137
133
  // first, we try to bind again within parameters - if we have it, fine