@eagleoutice/flowr 2.0.1 → 2.0.3

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 (78) hide show
  1. package/benchmark/slicer.d.ts +1 -0
  2. package/benchmark/slicer.js +69 -8
  3. package/benchmark/stats/print.d.ts +1 -0
  4. package/benchmark/stats/print.js +94 -31
  5. package/benchmark/stats/size-of.d.ts +3 -0
  6. package/benchmark/stats/size-of.js +68 -0
  7. package/benchmark/stats/stats.d.ts +23 -0
  8. package/benchmark/summarizer/data.d.ts +24 -1
  9. package/benchmark/summarizer/first-phase/input.d.ts +2 -2
  10. package/benchmark/summarizer/first-phase/input.js +21 -21
  11. package/benchmark/summarizer/first-phase/process.d.ts +4 -2
  12. package/benchmark/summarizer/first-phase/process.js +120 -33
  13. package/benchmark/summarizer/second-phase/graph.js +7 -0
  14. package/benchmark/summarizer/second-phase/process.js +65 -27
  15. package/benchmark/summarizer/summarizer.d.ts +1 -0
  16. package/benchmark/summarizer/summarizer.js +23 -10
  17. package/cli/repl/commands/commands.js +19 -1
  18. package/cli/slicer-app.js +1 -1
  19. package/dataflow/environments/append.js +1 -2
  20. package/dataflow/environments/built-in.js +2 -1
  21. package/dataflow/environments/clone.js +1 -1
  22. package/dataflow/environments/diff.d.ts +1 -1
  23. package/dataflow/environments/diff.js +16 -18
  24. package/dataflow/environments/environment.d.ts +6 -8
  25. package/dataflow/environments/environment.js +5 -8
  26. package/dataflow/environments/identifier.d.ts +2 -1
  27. package/dataflow/environments/overwrite.js +1 -2
  28. package/dataflow/environments/scoping.js +1 -1
  29. package/dataflow/graph/diff.js +11 -6
  30. package/dataflow/graph/edge.d.ts +2 -3
  31. package/dataflow/graph/edge.js +2 -2
  32. package/dataflow/graph/graph.d.ts +6 -2
  33. package/dataflow/graph/graph.js +16 -9
  34. package/dataflow/graph/vertex.d.ts +2 -1
  35. package/dataflow/info.d.ts +10 -1
  36. package/dataflow/info.js +54 -2
  37. package/dataflow/internal/linker.d.ts +1 -1
  38. package/dataflow/internal/linker.js +1 -2
  39. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +5 -5
  40. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +1 -1
  41. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +21 -25
  42. package/dataflow/internal/process/functions/call/built-in/built-in-get.js +6 -1
  43. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +10 -8
  44. package/dataflow/internal/process/functions/call/built-in/built-in-logical-bin-op.d.ts +1 -0
  45. package/dataflow/internal/process/functions/call/built-in/built-in-logical-bin-op.js +1 -2
  46. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +1 -1
  47. package/dataflow/internal/process/functions/call/default-call-handling.js +1 -1
  48. package/dataflow/internal/process/functions/call/unnamed-call-handling.js +1 -1
  49. package/dataflow/internal/process/process-value.js +0 -1
  50. package/dataflow/processor.d.ts +2 -3
  51. package/package.json +5 -2
  52. package/r-bridge/data/data.d.ts +1 -1
  53. package/r-bridge/data/data.js +1 -1
  54. package/r-bridge/lang-4.x/ast/model/nodes/r-function-call.d.ts +2 -2
  55. package/r-bridge/lang-4.x/ast/model/operators.js +1 -1
  56. package/r-bridge/lang-4.x/ast/model/processing/decorate.js +1 -1
  57. package/r-bridge/lang-4.x/ast/model/processing/stateful-fold.js +1 -1
  58. package/r-bridge/lang-4.x/ast/model/processing/visitor.js +2 -2
  59. package/r-bridge/lang-4.x/ast/parser/xml/internal/functions/normalize-call.js +2 -2
  60. package/r-bridge/lang-4.x/ast/parser/xml/internal/operators/normalize-binary.js +1 -1
  61. package/r-bridge/retriever.d.ts +1 -1
  62. package/r-bridge/retriever.js +3 -2
  63. package/r-bridge/shell.js +2 -1
  64. package/reconstruct/reconstruct.d.ts +3 -3
  65. package/reconstruct/reconstruct.js +40 -41
  66. package/slicing/criterion/filters/all-variables.js +1 -1
  67. package/slicing/static/static-slicer.js +2 -2
  68. package/statistics/features/common-syntax-probability.js +1 -1
  69. package/statistics/features/supported/control-flow/control-flow.js +1 -1
  70. package/statistics/features/supported/defined-functions/defined-functions.js +1 -1
  71. package/statistics/features/supported/loops/loops.js +1 -1
  72. package/statistics/features/supported/used-functions/used-functions.js +1 -1
  73. package/util/assert.d.ts +1 -1
  74. package/util/mermaid/ast.js +4 -0
  75. package/util/mermaid/dfg.d.ts +0 -1
  76. package/util/mermaid/dfg.js +16 -13
  77. package/util/mermaid/mermaid.js +21 -1
  78. package/util/version.js +1 -1
package/cli/slicer-app.js CHANGED
@@ -40,7 +40,7 @@ async function getSlice() {
40
40
  reconstruct = reconstructedCode;
41
41
  if (options.output) {
42
42
  console.log('Written reconstructed code to', options.output);
43
- console.log(`Automatically selected ${reconstructedCode.autoSelected} statements`);
43
+ console.log(`Automatically selected ${reconstructedCode.linesWithAutoSelected} lines`);
44
44
  fs_1.default.writeFileSync(options.output, reconstructedCode.code);
45
45
  }
46
46
  else if (!options.api && !options.diff) {
@@ -15,7 +15,6 @@ function uniqueMergeValues(old, value) {
15
15
  }
16
16
  function appendIEnvironmentWith(base, next) {
17
17
  (0, assert_1.guard)(base !== undefined && next !== undefined, 'can not append environments with undefined');
18
- (0, assert_1.guard)(base.name === next.name, 'cannot overwrite environments with different names');
19
18
  const map = new Map(base.memory);
20
19
  for (const [key, value] of next.memory) {
21
20
  const old = map.get(key);
@@ -27,7 +26,7 @@ function appendIEnvironmentWith(base, next) {
27
26
  }
28
27
  }
29
28
  const parent = base.parent === environment_1.BuiltInEnvironment ? environment_1.BuiltInEnvironment : appendIEnvironmentWith(base.parent, next.parent);
30
- const out = new environment_1.Environment(base.name, parent);
29
+ const out = new environment_1.Environment(parent);
31
30
  out.memory = map;
32
31
  return out;
33
32
  }
@@ -111,7 +111,8 @@ registerBuiltInFunctions(['delayedAssign'], built_in_assignment_1.processAssignm
111
111
  registerBuiltInFunctions(['<<-'], built_in_assignment_1.processAssignment, { superAssignment: true, canBeReplacement: true });
112
112
  registerBuiltInFunctions(['->'], built_in_assignment_1.processAssignment, { swapSourceAndTarget: true, canBeReplacement: true });
113
113
  registerBuiltInFunctions(['->>'], built_in_assignment_1.processAssignment, { superAssignment: true, swapSourceAndTarget: true, canBeReplacement: true });
114
- registerBuiltInFunctions(['&&', '||', '&', '|'], built_in_logical_bin_op_1.processSpecialBinOp, { lazy: true });
114
+ registerBuiltInFunctions(['&&', '&'], built_in_logical_bin_op_1.processSpecialBinOp, { lazy: true, evalRhsWhen: true });
115
+ registerBuiltInFunctions(['||', '|'], built_in_logical_bin_op_1.processSpecialBinOp, { lazy: true, evalRhsWhen: false });
115
116
  registerBuiltInFunctions(['|>', '%>%'], built_in_pipe_1.processPipe, {});
116
117
  registerBuiltInFunctions(['function', '\\'], built_in_function_definition_1.processFunctionDefinition, {});
117
118
  registerBuiltInFunctions(['quote', 'substitute', 'bquote'], built_in_quote_1.processQuote, { quoteArgumentsWithIndex: 0 });
@@ -9,7 +9,7 @@ function cloneEnvironment(environment, recurseParents) {
9
9
  else if (environment.id === environment_1.BuiltInEnvironment.id) {
10
10
  return environment_1.BuiltInEnvironment;
11
11
  }
12
- const clone = new environment_1.Environment(environment.name, recurseParents ? cloneEnvironment(environment.parent, recurseParents) : environment.parent);
12
+ const clone = new environment_1.Environment(recurseParents ? cloneEnvironment(environment.parent, recurseParents) : environment.parent);
13
13
  clone.memory = new Map(JSON.parse(JSON.stringify([...environment.memory])));
14
14
  return clone;
15
15
  }
@@ -2,5 +2,5 @@ import type { GenericDifferenceInformation, WriteableDifferenceReport } from '..
2
2
  import type { IEnvironment, REnvironmentInformation } from './environment';
3
3
  import type { IdentifierReference } from './identifier';
4
4
  export declare function diffIdentifierReferences<Report extends WriteableDifferenceReport>(a: IdentifierReference | undefined, b: IdentifierReference | undefined, info: GenericDifferenceInformation<Report>): void;
5
- export declare function diffEnvironment<Report extends WriteableDifferenceReport>(a: IEnvironment | undefined, b: IEnvironment | undefined, info: GenericDifferenceInformation<Report>): void;
5
+ export declare function diffEnvironment<Report extends WriteableDifferenceReport>(a: IEnvironment | undefined, b: IEnvironment | undefined, info: GenericDifferenceInformation<Report>, depth: number): void;
6
6
  export declare function diffEnvironmentInformation<Report extends WriteableDifferenceReport>(a: REnvironmentInformation | undefined, b: REnvironmentInformation | undefined, info: GenericDifferenceInformation<Report>): void;
@@ -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,26 +6,25 @@
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
+ export type EnvironmentMemory = Map<Identifier, IdentifierDefinition[]>;
12
13
  export interface IEnvironment {
13
14
  /** unique and internally generated identifier -- will not be used for comparison but assists debugging for tracking identities */
14
15
  readonly id: string;
15
- readonly name: string;
16
16
  /** Lexical parent of the environment, if any (can be manipulated by R code) */
17
17
  parent: IEnvironment;
18
18
  /**
19
19
  * Maps to exactly one definition of an identifier if the source is known, otherwise to a list of all possible definitions
20
20
  */
21
- memory: Map<Identifier, IdentifierDefinition[]>;
21
+ memory: EnvironmentMemory;
22
22
  }
23
23
  export declare class Environment implements IEnvironment {
24
- readonly name: string;
25
24
  readonly id: string;
26
25
  parent: IEnvironment;
27
26
  memory: Map<Identifier, IdentifierDefinition[]>;
28
- constructor(name: string, parent: IEnvironment);
27
+ constructor(parent: IEnvironment);
29
28
  }
30
29
  /**
31
30
  * First of all, yes, R stores its environments differently, potentially even with a different differentiation between
@@ -41,5 +40,4 @@ export interface REnvironmentInformation {
41
40
  readonly level: number;
42
41
  }
43
42
  export declare const BuiltInEnvironment: Environment;
44
- export declare const GLOBAL_ENV_NAME = "global";
45
43
  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 */) {
@@ -2,7 +2,6 @@
2
2
  * An edge consist of:
3
3
  * - the target node (i.e., the variable or processing node),
4
4
  * - a type (if it is read or used in the context), and
5
- * - an attribute (if this edge exists for every program execution or if it is only one possible execution path).
6
5
  */
7
6
  export interface DataflowGraphEdge {
8
7
  types: EdgeTypeBits;
@@ -40,8 +39,8 @@ export declare const enum EdgeTypeName {
40
39
  DefinedBy = "defined-by",
41
40
  Calls = "calls",
42
41
  Returns = "returns",
43
- DefinesOnCall = "defined-by-on-call",
44
- DefinedByOnCall = "defines-on-call",
42
+ DefinesOnCall = "defines-on-call",
43
+ DefinedByOnCall = "defined-by-on-call",
45
44
  Argument = "argument",
46
45
  SideEffectOnCall = "side-effect-on-call",
47
46
  NonStandardEvaluation = "non-standard-evaluation"
@@ -6,8 +6,8 @@ const edgeTypeToHumanReadableName = new Map([
6
6
  [2 /* EdgeType.DefinedBy */, "defined-by" /* EdgeTypeName.DefinedBy */],
7
7
  [4 /* EdgeType.Calls */, "calls" /* EdgeTypeName.Calls */],
8
8
  [8 /* EdgeType.Returns */, "returns" /* EdgeTypeName.Returns */],
9
- [16 /* EdgeType.DefinesOnCall */, "defined-by-on-call" /* EdgeTypeName.DefinesOnCall */],
10
- [32 /* EdgeType.DefinedByOnCall */, "defines-on-call" /* EdgeTypeName.DefinedByOnCall */],
9
+ [16 /* EdgeType.DefinesOnCall */, "defines-on-call" /* EdgeTypeName.DefinesOnCall */],
10
+ [32 /* EdgeType.DefinedByOnCall */, "defined-by-on-call" /* EdgeTypeName.DefinedByOnCall */],
11
11
  [64 /* EdgeType.Argument */, "argument" /* EdgeTypeName.Argument */],
12
12
  [128 /* EdgeType.SideEffectOnCall */, "side-effect-on-call" /* EdgeTypeName.SideEffectOnCall */],
13
13
  [256 /* EdgeType.NonStandardEvaluation */, "non-standard-evaluation" /* EdgeTypeName.NonStandardEvaluation */]
@@ -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
  */
@@ -7,6 +7,7 @@ const arrays_1 = require("../../util/arrays");
7
7
  const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
8
8
  const environment_1 = require("../environments/environment");
9
9
  const clone_1 = require("../environments/clone");
10
+ const built_in_1 = require("../environments/built-in");
10
11
  function isPositionalArgument(arg) {
11
12
  return arg !== r_function_call_1.EmptyArgument && arg.name === undefined;
12
13
  }
@@ -40,10 +41,10 @@ function extractEdgeIds(from, to) {
40
41
  */
41
42
  class DataflowGraph {
42
43
  static DEFAULT_ENVIRONMENT = undefined;
43
- idMap;
44
+ _idMap;
44
45
  constructor(idMap) {
45
46
  DataflowGraph.DEFAULT_ENVIRONMENT ??= (0, environment_1.initializeCleanEnvironments)();
46
- this.idMap = idMap;
47
+ this._idMap = idMap;
47
48
  }
48
49
  /** Contains the vertices of the root level graph (i.e., included those vertices from the complete graph, that are nested within function definitions) */
49
50
  rootVertices = new Set();
@@ -89,6 +90,14 @@ class DataflowGraph {
89
90
  }
90
91
  return edges;
91
92
  }
93
+ /** Retrieves the id-map to the normalized AST attached to the dataflow graph */
94
+ get idMap() {
95
+ return this._idMap;
96
+ }
97
+ /** 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) */
98
+ setIdMap(idMap) {
99
+ this._idMap = idMap;
100
+ }
92
101
  /**
93
102
  * @param includeDefinedFunctions - If true this will iterate over function definitions as well and not just the toplevel
94
103
  * @returns the ids of all toplevel vertices in the graph together with their vertex information
@@ -119,7 +128,7 @@ class DataflowGraph {
119
128
  * @param id - The id to check for
120
129
  * @param includeDefinedFunctions - If true this will check function definitions as well and not just the toplevel
121
130
  */
122
- hasVertex(id, includeDefinedFunctions) {
131
+ hasVertex(id, includeDefinedFunctions = true) {
123
132
  return includeDefinedFunctions ? this.vertexInformation.has(id) : this.rootVertices.has(id);
124
133
  }
125
134
  /**
@@ -146,11 +155,11 @@ class DataflowGraph {
146
155
  if (oldVertex !== undefined) {
147
156
  return this;
148
157
  }
158
+ const fallback = vertex.tag === "variable-definition" /* VertexType.VariableDefinition */ || vertex.tag === "use" /* VertexType.Use */ || vertex.tag === "value" /* VertexType.Value */ ? undefined : DataflowGraph.DEFAULT_ENVIRONMENT;
149
159
  // keep a clone of the original environment
150
- const environment = vertex.environment === undefined ? DataflowGraph.DEFAULT_ENVIRONMENT : (0, clone_1.cloneEnvironmentInformation)(vertex.environment);
160
+ const environment = vertex.environment === undefined ? fallback : (0, clone_1.cloneEnvironmentInformation)(vertex.environment);
151
161
  this.vertexInformation.set(vertex.id, {
152
162
  ...vertex,
153
- when: vertex.controlDependencies ?? 'always',
154
163
  environment
155
164
  });
156
165
  if (asRoot) {
@@ -162,11 +171,12 @@ class DataflowGraph {
162
171
  * Will insert a new edge into the graph,
163
172
  * if the direction of the edge is of no importance (`same-read-read` or `same-def-def`), source
164
173
  * and target will be sorted so that `from` has the lower, and `to` the higher id (default ordering).
174
+ * Please note, that this will never make edges to {@link BuiltIn} as they are not part of the graph.
165
175
  */
166
176
  addEdge(from, to, edgeInfo) {
167
177
  const { fromId, toId } = extractEdgeIds(from, to);
168
178
  const { type, ...rest } = edgeInfo;
169
- if (fromId === toId) {
179
+ if (fromId === toId || toId === built_in_1.BuiltIn) {
170
180
  return this;
171
181
  }
172
182
  /* we now that we pass all required arguments */
@@ -263,9 +273,6 @@ class DataflowGraph {
263
273
  const vertex = this.getVertex(reference.nodeId, true);
264
274
  (0, assert_1.guard)(vertex !== undefined, () => `node must be defined for ${JSON.stringify(reference)} to set reference`);
265
275
  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
276
  vertex.controlDependencies = reference.controlDependencies;
270
277
  }
271
278
  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);