@eagleoutice/flowr 2.0.10 → 2.0.12

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 (40) hide show
  1. package/README.md +41 -8
  2. package/abstract-interpretation/domain.d.ts +33 -3
  3. package/abstract-interpretation/domain.js +31 -1
  4. package/benchmark/slicer.d.ts +3 -0
  5. package/benchmark/slicer.js +3 -0
  6. package/benchmark/stats/print.js +0 -1
  7. package/benchmark/summarizer/second-phase/graph.js +4 -4
  8. package/cli/flowr.d.ts +9 -8
  9. package/cli/flowr.js +22 -14
  10. package/cli/repl/commands/parse.js +1 -1
  11. package/cli/repl/core.d.ts +28 -12
  12. package/cli/repl/core.js +18 -18
  13. package/cli/repl/server/connection.d.ts +2 -1
  14. package/cli/repl/server/connection.js +61 -18
  15. package/cli/repl/server/messages/analysis.d.ts +11 -5
  16. package/cli/repl/server/messages/analysis.js +1 -1
  17. package/cli/repl/server/server.d.ts +2 -1
  18. package/cli/repl/server/server.js +4 -2
  19. package/core/steps/all/core/00-parse.d.ts +3 -3
  20. package/core/steps/all/core/00-parse.js +7 -1
  21. package/core/steps/all/core/20-dataflow.d.ts +2 -2
  22. package/core/steps/pipeline/default-pipelines.d.ts +8 -8
  23. package/dataflow/environments/built-in.js +1 -1
  24. package/dataflow/extractor.d.ts +2 -2
  25. package/dataflow/extractor.js +20 -4
  26. package/dataflow/info.d.ts +9 -3
  27. package/dataflow/internal/process/functions/call/built-in/built-in-source.d.ts +8 -2
  28. package/dataflow/internal/process/functions/call/built-in/built-in-source.js +25 -4
  29. package/dataflow/processor.d.ts +5 -4
  30. package/dataflow/processor.js +3 -2
  31. package/package.json +7 -5
  32. package/r-bridge/data/types.d.ts +2 -2
  33. package/r-bridge/lang-4.x/ast/model/processing/decorate.d.ts +1 -0
  34. package/r-bridge/lang-4.x/ast/model/processing/decorate.js +5 -1
  35. package/r-bridge/lang-4.x/ast/parser/xml/internal/loops/normalize-repeat.d.ts +2 -2
  36. package/r-bridge/lang-4.x/ast/parser/xml/internal/loops/normalize-repeat.js +2 -2
  37. package/r-bridge/retriever.d.ts +24 -16
  38. package/r-bridge/retriever.js +19 -8
  39. package/slicing/criterion/parse.js +4 -4
  40. package/util/version.js +1 -1
package/README.md CHANGED
@@ -1,23 +1,56 @@
1
- [![flowR logo](https://raw.githubusercontent.com/wiki/Code-Inspect/flowr/img/flowR.png)](https://github.com/Code-Inspect/flowr/wiki)\
2
- [![QA (and potentially deploy)](https://github.com/Code-Inspect/flowr/actions/workflows/qa.yaml/badge.svg)](https://github.com/Code-Inspect/flowr/actions/workflows/qa.yaml) [![codecov](https://codecov.io/gh/Code-Inspect/flowr/graph/badge.svg)](https://codecov.io/gh/Code-Inspect/flowr) [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/eagleoutice/flowr?logo=docker&logoColor=white&label=dockerhub)](https://hub.docker.com/r/eagleoutice/flowr) [![latest tag](https://badgen.net/github/tag/Code-Inspect/flowr?label=latest&color=purple)](https://github.com/Code-Inspect/flowr/releases/latest) [![Marketplace](https://badgen.net/vs-marketplace/v/code-inspect.vscode-flowr)](https://marketplace.visualstudio.com/items?itemName=code-inspect.vscode-flowr)
1
+ [![flowR logo](https://raw.githubusercontent.com/wiki/flowr-analysis/flowr/img/flowR.png)](https://github.com/flowr-analysis/flowr/wiki)\
2
+ [![QA (and potentially deploy)](https://github.com/flowr-analysis/flowr/actions/workflows/qa.yaml/badge.svg)](https://github.com/flowr-analysis/flowr/actions/workflows/qa.yaml) [![codecov](https://codecov.io/gh/flowr-analysis/flowr/graph/badge.svg)](https://codecov.io/gh/flowr-analysis/flowr) [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/eagleoutice/flowr?logo=docker&logoColor=white&label=dockerhub)](https://hub.docker.com/r/eagleoutice/flowr) [![latest tag](https://badgen.net/github/tag/flowr-analysis/flowr?label=latest&color=purple)](https://github.com/flowr-analysis/flowr/releases/latest) [![Marketplace](https://badgen.net/vs-marketplace/v/code-inspect.vscode-flowr)](https://marketplace.visualstudio.com/items?itemName=code-inspect.vscode-flowr) [![All Contributors](https://img.shields.io/github/all-contributors/flowr-analysis/flowr)](#contributors)
3
3
 
4
4
 
5
- *flowR* is a static [dataflow analyzer](https://en.wikipedia.org/wiki/Data-flow_analysis) and [program slicer](https://github.com/Code-Inspect/flowr/wiki/Terminology#program-slice) for the [*R*](https://www.r-project.org/) programming language (currently tested for versions `4.x` and `3.6.x`). It is available as a [Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=code-inspect.vscode-flowr) and as a [docker image](https://hub.docker.com/r/eagleoutice/flowr).
5
+ *flowR* is a static [dataflow analyzer](https://en.wikipedia.org/wiki/Data-flow_analysis) and [program slicer](https://github.com/flowr-analysis/flowr/wiki/Terminology#program-slice) for the [*R*](https://www.r-project.org/) programming language (currently tested for versions `4.x` and `3.6.x`). It is available as a [Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=code-inspect.vscode-flowr) and as a [docker image](https://hub.docker.com/r/eagleoutice/flowr).
6
6
 
7
7
  ## ⭐ Getting Started
8
8
 
9
- To get started with _flowR_, please check out the [Overview](https://github.com/Code-Inspect/flowr/wiki/Overview). The [Setup](https://github.com/Code-Inspect/flowr/wiki/Setup) wiki page explains how you can download and setup _flowR_ on your system. For Visual Studio Code, please see the [marketplace entry](https://marketplace.visualstudio.com/items?itemName=code-inspect.vscode-flowr). With docker 🐳️, the following line should be enough (and drop you directly into the REPL):
9
+ To get started with _flowR_, please check out the [Overview](https://github.com/flowr-analysis/flowr/wiki/Overview). The [Setup](https://github.com/flowr-analysis/flowr/wiki/Setup) wiki page explains how you can download and setup _flowR_ on your system. For Visual Studio Code, please see the [marketplace entry](https://marketplace.visualstudio.com/items?itemName=code-inspect.vscode-flowr). With docker 🐳️, the following line should be enough (and drop you directly into the REPL):
10
10
 
11
11
  ```shell
12
12
  docker run -it --rm eagleoutice/flowr
13
13
  ```
14
14
 
15
-
16
15
  ## 📜 More Information
17
16
 
18
- For more details, see the [wiki pages](https://github.com/Code-Inspect/flowr/wiki) and the deployed [code documentation](https://code-inspect.github.io/flowr/doc/).
19
-
20
- We welcome every contribution! Please check out the [contributing guidelines](https://github.com/Code-Inspect/flowr/tree/main/.github/CONTRIBUTING.md) for more information.
17
+ For more details, see the [wiki pages](https://github.com/flowr-analysis/flowr/wiki) and the deployed [code documentation](https://flowr-analysis.github.io/flowr/doc/).
18
+
19
+ ## 🚀 Contributing
20
+
21
+ We welcome every contribution! Please check out the [contributing guidelines](https://github.com/flowr-analysis/flowr/tree/main/.github/CONTRIBUTING.md) for more information.
22
+
23
+ ### Contributors
24
+
25
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
26
+ <!--suppress ALL -->
27
+ <!-- prettier-ignore-start -->
28
+ <!-- markdownlint-disable -->
29
+ <table>
30
+ <tbody>
31
+ <tr>
32
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/EagleoutIce"><img src="https://avatars.githubusercontent.com/u/9303573?v=4?s=100" width="100px;" alt="Florian Sihler"/><br /><sub><b>Florian Sihler</b></sub></a><br /><a href="https://github.com/flowr-analysis/flowr/commits?author=EagleoutIce" title="Code">💻</a> <a href="#ideas-EagleoutIce" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-EagleoutIce" title="Maintenance">🚧</a> <a href="#projectManagement-EagleoutIce" title="Project Management">📆</a> <a href="#research-EagleoutIce" title="Research">🔬</a> <a href="https://github.com/flowr-analysis/flowr/commits?author=EagleoutIce" title="Tests">⚠️</a> <a href="#talk-EagleoutIce" title="Talks">📢</a></td>
33
+ <td align="center" valign="top" width="14.28%"><a href="https://ellpeck.de/"><img src="https://avatars.githubusercontent.com/u/5741138?v=4?s=100" width="100px;" alt="Ell"/><br /><sub><b>Ell</b></sub></a><br /><a href="https://github.com/flowr-analysis/flowr/commits?author=Ellpeck" title="Code">💻</a> <a href="#maintenance-Ellpeck" title="Maintenance">🚧</a> <a href="https://github.com/flowr-analysis/flowr/commits?author=Ellpeck" title="Tests">⚠️</a> <a href="#plugin-Ellpeck" title="Plugin/utility libraries">🔌</a></td>
34
+ <td align="center" valign="top" width="14.28%"><a href="https://lukas.pietzschmann.org/"><img src="https://avatars.githubusercontent.com/u/49213919?v=4?s=100" width="100px;" alt="Lukas Pietzschmann"/><br /><sub><b>Lukas Pietzschmann</b></sub></a><br /><a href="https://github.com/flowr-analysis/flowr/commits?author=LukasPietzschmann" title="Code">💻</a> <a href="https://github.com/flowr-analysis/flowr/commits?author=LukasPietzschmann" title="Tests">⚠️</a></td>
35
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/bjthehun"><img src="https://avatars.githubusercontent.com/u/38729215?v=4?s=100" width="100px;" alt="Benedikt Jutz"/><br /><sub><b>Benedikt Jutz</b></sub></a><br /><a href="https://github.com/flowr-analysis/flowr/commits?author=bjthehun" title="Code">💻</a> <a href="https://github.com/flowr-analysis/flowr/commits?author=bjthehun" title="Tests">⚠️</a></td>
36
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/Core5563"><img src="https://avatars.githubusercontent.com/u/140061253?v=4?s=100" width="100px;" alt="Core5563"/><br /><sub><b>Core5563</b></sub></a><br /><a href="https://github.com/flowr-analysis/flowr/commits?author=Core5563" title="Code">💻</a> <a href="https://github.com/flowr-analysis/flowr/commits?author=Core5563" title="Tests">⚠️</a></td>
37
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/Ehcsan"><img src="https://avatars.githubusercontent.com/u/68707578?v=4?s=100" width="100px;" alt="Ehcsan"/><br /><sub><b>Ehcsan</b></sub></a><br /><a href="https://github.com/flowr-analysis/flowr/commits?author=Ehcsan" title="Code">💻</a> <a href="https://github.com/flowr-analysis/flowr/commits?author=Ehcsan" title="Tests">⚠️</a></td>
38
+ </tr>
39
+ </tbody>
40
+ <tfoot>
41
+ <tr>
42
+ <td align="center" size="13px" colspan="7">
43
+ <img src="https://raw.githubusercontent.com/all-contributors/all-contributors-cli/1b8533af435da9854653492b1327a23a4dbd0a10/assets/logo-small.svg">
44
+ <a href="https://all-contributors.js.org/docs/en/bot/usage">Add your contributions</a>
45
+ </img>
46
+ </td>
47
+ </tr>
48
+ </tfoot>
49
+ </table>
50
+
51
+ <!-- markdownlint-restore -->
52
+ <!-- prettier-ignore-end -->
53
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
21
54
 
22
55
  ----
23
56
 
@@ -5,23 +5,53 @@ interface IntervalBound {
5
5
  export declare class Interval {
6
6
  readonly min: IntervalBound;
7
7
  readonly max: IntervalBound;
8
+ /**
9
+ * Build a new interval from the given bounds.
10
+ * If the interval represents a scalar, the min and max bounds should be equal, as well as their inclusivity.
11
+ * @param min - The minimum bound of the interval.
12
+ * @param max - The maximum bound of the interval.
13
+ */
8
14
  constructor(min: IntervalBound, max: IntervalBound);
9
15
  toString(): string;
10
16
  }
17
+ /**
18
+ * A domain represents a set of intervals describing a range of possible values a variable may hold.
19
+ * In the future we may want to extend this to support more complex types.
20
+ */
11
21
  export declare class Domain {
22
+ /** set of intervals to hold */
12
23
  private readonly _intervals;
13
24
  private constructor();
14
25
  static bottom(): Domain;
15
- static fromIntervals(intervals: Interval[] | Set<Interval>): Domain;
26
+ static fromIntervals(intervals: readonly Interval[] | ReadonlySet<Interval>): Domain;
16
27
  static fromScalar(n: number): Domain;
17
28
  get intervals(): Set<Interval>;
18
29
  private set intervals(value);
19
30
  addInterval(interval: Interval): void;
20
31
  toString(): string;
21
32
  }
33
+ /**
34
+ * Returns true if the given intervals overlap, checking for inclusivity.
35
+ */
22
36
  export declare function doIntervalsOverlap(interval1: Interval, interval2: Interval): boolean;
23
- export declare function unifyDomains(domains: Domain[]): Domain;
24
- export declare function unifyOverlappingIntervals(intervals: Interval[]): Interval[];
37
+ /**
38
+ * Unifies the given domains by creating a new domain that contains all values from all given domains.
39
+ */
40
+ export declare function unifyDomains(domains: readonly Domain[]): Domain;
41
+ /**
42
+ * Unify all intervals which overlap with each other to one.
43
+ */
44
+ export declare function unifyOverlappingIntervals(intervals: readonly Interval[]): Interval[];
45
+ /**
46
+ * Returns domain1 + domain2, mapping the inclusivity.
47
+ *
48
+ * @see subtractDomains
49
+ */
25
50
  export declare function addDomains(domain1: Domain, domain2: Domain): Domain;
51
+ /**
52
+ * Returns domain1 - domain2, mapping the inclusivity.
53
+ *
54
+ * @see addDomains
55
+ */
26
56
  export declare function subtractDomains(domain1: Domain, domain2: Domain): Domain;
27
57
  export {};
@@ -5,6 +5,12 @@ const assert_1 = require("../util/assert");
5
5
  class Interval {
6
6
  min;
7
7
  max;
8
+ /**
9
+ * Build a new interval from the given bounds.
10
+ * If the interval represents a scalar, the min and max bounds should be equal, as well as their inclusivity.
11
+ * @param min - The minimum bound of the interval.
12
+ * @param max - The maximum bound of the interval.
13
+ */
8
14
  constructor(min, max) {
9
15
  this.min = min;
10
16
  this.max = max;
@@ -16,7 +22,12 @@ class Interval {
16
22
  }
17
23
  }
18
24
  exports.Interval = Interval;
25
+ /**
26
+ * A domain represents a set of intervals describing a range of possible values a variable may hold.
27
+ * In the future we may want to extend this to support more complex types.
28
+ */
19
29
  class Domain {
30
+ /** set of intervals to hold */
20
31
  _intervals;
21
32
  constructor(intervals = []) {
22
33
  this._intervals = new Set(unifyOverlappingIntervals(intervals));
@@ -67,6 +78,9 @@ function compareIntervalsByTheirMinimum(interval1, interval2) {
67
78
  function compareIntervalsByTheirMaximum(interval1, interval2) {
68
79
  return compareIntervals(1 /* CompareType.Max */, interval1.max, interval2.max);
69
80
  }
81
+ /**
82
+ * Returns true if the given intervals overlap, checking for inclusivity.
83
+ */
70
84
  function doIntervalsOverlap(interval1, interval2) {
71
85
  const diff1 = compareIntervals(2 /* CompareType.IgnoreInclusivity */, interval1.max, interval2.min);
72
86
  const diff2 = compareIntervals(2 /* CompareType.IgnoreInclusivity */, interval2.max, interval1.min);
@@ -84,16 +98,22 @@ function doIntervalsOverlap(interval1, interval2) {
84
98
  return true;
85
99
  }
86
100
  exports.doIntervalsOverlap = doIntervalsOverlap;
101
+ /**
102
+ * Unifies the given domains by creating a new domain that contains all values from all given domains.
103
+ */
87
104
  function unifyDomains(domains) {
88
105
  const unifiedIntervals = unifyOverlappingIntervals(domains.flatMap(domain => Array.from(domain.intervals)));
89
106
  return Domain.fromIntervals(unifiedIntervals);
90
107
  }
91
108
  exports.unifyDomains = unifyDomains;
109
+ /**
110
+ * Unify all intervals which overlap with each other to one.
111
+ */
92
112
  function unifyOverlappingIntervals(intervals) {
93
113
  if (intervals.length === 0) {
94
114
  return [];
95
115
  }
96
- const sortedIntervals = intervals.sort(compareIntervalsByTheirMinimum);
116
+ const sortedIntervals = [...intervals].sort(compareIntervalsByTheirMinimum);
97
117
  const unifiedIntervals = [];
98
118
  let currentInterval = sortedIntervals[0];
99
119
  for (const nextInterval of sortedIntervals) {
@@ -111,6 +131,11 @@ function unifyOverlappingIntervals(intervals) {
111
131
  return unifiedIntervals;
112
132
  }
113
133
  exports.unifyOverlappingIntervals = unifyOverlappingIntervals;
134
+ /**
135
+ * Returns domain1 + domain2, mapping the inclusivity.
136
+ *
137
+ * @see subtractDomains
138
+ */
114
139
  function addDomains(domain1, domain2) {
115
140
  const intervals = new Set();
116
141
  for (const interval1 of domain1.intervals) {
@@ -127,6 +152,11 @@ function addDomains(domain1, domain2) {
127
152
  return Domain.fromIntervals(intervals);
128
153
  }
129
154
  exports.addDomains = addDomains;
155
+ /**
156
+ * Returns domain1 - domain2, mapping the inclusivity.
157
+ *
158
+ * @see addDomains
159
+ */
130
160
  function subtractDomains(domain1, domain2) {
131
161
  const intervals = new Set();
132
162
  for (const interval1 of domain1.intervals) {
@@ -11,6 +11,9 @@ import type { NormalizedAst } from '../r-bridge/lang-4.x/ast/model/processing/de
11
11
  import type { SlicingCriteria } from '../slicing/criterion/parse';
12
12
  import type { RParseRequestFromFile, RParseRequestFromText } from '../r-bridge/retriever';
13
13
  import type { SlicingCriteriaFilter } from '../slicing/criterion/collect-all';
14
+ /**
15
+ * The logger to be used for benchmarking as a global object.
16
+ */
14
17
  export declare const benchmarkLogger: import("tslog").Logger<import("tslog").ILogObj>;
15
18
  /**
16
19
  * Returns the stats but also the result of all setup steps (parsing, normalization, and the dataflow analysis) during the slicing.
@@ -20,6 +20,9 @@ const retriever_1 = require("../r-bridge/retriever");
20
20
  const collect_all_1 = require("../slicing/criterion/collect-all");
21
21
  const visitor_1 = require("../r-bridge/lang-4.x/ast/model/processing/visitor");
22
22
  const size_of_1 = require("./stats/size-of");
23
+ /**
24
+ * The logger to be used for benchmarking as a global object.
25
+ */
23
26
  exports.benchmarkLogger = log_1.log.getSubLogger({ name: 'benchmark' });
24
27
  /**
25
28
  * A slicer that can be used to slice exactly one file (multiple times).
@@ -46,7 +46,6 @@ function asPercentage(num) {
46
46
  if (isNaN(num)) {
47
47
  return '??%';
48
48
  }
49
- (0, assert_1.guard)(num >= 0 && num <= 1, `Percentage ${num} should be between 0 and 1`);
50
49
  return pad(`${roundTo(num * 100, 3)}%`);
51
50
  }
52
51
  function asFloat(num) {
@@ -49,10 +49,10 @@ function writeGraphOutput(ultimate, outputGraphPath) {
49
49
  });
50
50
  data.push({
51
51
  name: 'memory (df-graph)',
52
- unit: 'Bytes',
53
- value: Number(ultimate.dataflow.sizeOfObject.mean),
54
- range: Number(ultimate.dataflow.sizeOfObject.std),
55
- extra: `median: ${(ultimate.dataflow.sizeOfObject.median).toFixed(2)}`
52
+ unit: 'KiB',
53
+ value: ultimate.dataflow.sizeOfObject.mean / 1024,
54
+ range: ultimate.dataflow.sizeOfObject.std / 1024,
55
+ extra: `median: ${(ultimate.dataflow.sizeOfObject.median / 1024).toFixed(2)}`
56
56
  });
57
57
  // write the output file
58
58
  fs_1.default.writeFileSync(outputGraphPath, JSON.stringify(data, json_1.jsonReplacer));
package/cli/flowr.d.ts CHANGED
@@ -2,17 +2,18 @@ import type { OptionDefinition } from 'command-line-usage';
2
2
  export declare const toolName = "flowr";
3
3
  export declare const optionDefinitions: OptionDefinition[];
4
4
  export interface FlowrCliOptions {
5
- verbose: boolean;
6
- version: boolean;
7
- help: boolean;
8
- server: boolean;
9
- ws: boolean;
10
- port: number;
5
+ 'config-file': string;
11
6
  'no-ansi': boolean;
7
+ 'r-path': string | undefined;
8
+ 'r-session-access': boolean;
12
9
  execute: string | undefined;
10
+ help: boolean;
11
+ port: number;
13
12
  script: string | undefined;
14
- 'config-file': string;
15
- 'r-path': string | undefined;
13
+ server: boolean;
14
+ verbose: boolean;
15
+ version: boolean;
16
+ ws: boolean;
16
17
  }
17
18
  export declare const optionHelp: ({
18
19
  header: string;
package/cli/flowr.js CHANGED
@@ -20,20 +20,27 @@ const main_1 = require("./repl/commands/main");
20
20
  const core_1 = require("./repl/core");
21
21
  const version_2 = require("./repl/commands/version");
22
22
  const print_version_1 = require("./repl/print-version");
23
- const scriptsText = Array.from(Object.entries(scripts_info_1.scripts).filter(([, { type }]) => type === 'master script'), ([k,]) => k).join(', ');
23
+ let _scriptsText;
24
+ function getScriptsText() {
25
+ if (_scriptsText === undefined) {
26
+ _scriptsText = Array.from(Object.entries(scripts_info_1.scripts).filter(([, { type }]) => type === 'master script'), ([k,]) => k).join(', ');
27
+ }
28
+ return _scriptsText;
29
+ }
24
30
  exports.toolName = 'flowr';
25
31
  exports.optionDefinitions = [
26
- { name: 'verbose', alias: 'v', type: Boolean, description: 'Run with verbose logging (will be passed to the corresponding script)' },
27
- { name: 'help', alias: 'h', type: Boolean, description: 'Print this usage guide (or the guide of the corresponding script)' },
28
- { name: 'version', alias: 'V', type: Boolean, description: 'Provide information about the version of flowR as well as its underlying R system and exit.' },
29
- { name: 'server', type: Boolean, description: 'Do not drop into a repl, but instead start a server on the given port (default: 1042) and listen for messages.' },
30
- { name: 'ws', type: Boolean, description: 'If the server flag is set, use websocket for messaging' },
31
- { name: 'port', type: Number, description: 'The port to listen on, if --server is given.', defaultValue: 1042, typeLabel: '{underline port}' },
32
+ { name: 'config-file', type: String, description: 'The name of the configuration file to use', multiple: false },
32
33
  { name: 'execute', alias: 'e', type: String, description: 'Execute the given command and exit. Use a semicolon ";" to separate multiple commands.', typeLabel: '{underline command}', multiple: false },
34
+ { name: 'help', alias: 'h', type: Boolean, description: 'Print this usage guide (or the guide of the corresponding script)' },
33
35
  { name: 'no-ansi', type: Boolean, description: 'Disable ansi-escape-sequences in the output. Useful, if you want to redirect the output to a file.' },
34
- { name: 'script', alias: 's', type: String, description: `The sub-script to run (${scriptsText})`, multiple: false, defaultOption: true, typeLabel: '{underline files}', defaultValue: undefined },
35
- { name: 'config-file', type: String, description: 'The name of the configuration file to use', multiple: false },
36
- { name: 'r-path', type: String, description: 'The path to the R executable to use. Defaults to your PATH.', multiple: false }
36
+ { name: 'port', type: Number, description: 'The port to listen on, if --server is given.', defaultValue: 1042, typeLabel: '{underline port}' },
37
+ { name: 'r-path', type: String, description: 'The path to the R executable to use. Defaults to your PATH.', multiple: false },
38
+ { name: 'r-session-access', type: Boolean, description: 'Allow to access the underlying R session when using flowR (security warning: this allows the execution of arbitrary R code!)' },
39
+ { name: 'script', alias: 's', type: String, description: `The sub-script to run (${getScriptsText()})`, multiple: false, defaultOption: true, typeLabel: '{underline files}', defaultValue: undefined },
40
+ { name: 'server', type: Boolean, description: 'Do not drop into a repl, but instead start a server on the given port (default: 1042) and listen for messages.' },
41
+ { name: 'verbose', alias: 'v', type: Boolean, description: 'Run with verbose logging (will be passed to the corresponding script)' },
42
+ { name: 'version', alias: 'V', type: Boolean, description: 'Provide information about the version of flowR as well as its underlying R system and exit.' },
43
+ { name: 'ws', type: Boolean, description: 'If the server flag is set, use websocket for messaging' }
37
44
  ];
38
45
  exports.optionHelp = [
39
46
  {
@@ -81,7 +88,7 @@ function retrieveShell() {
81
88
  async function mainRepl() {
82
89
  if (options.script) {
83
90
  let target = scripts_info_1.scripts[options.script].target;
84
- (0, assert_1.guard)(target !== undefined, `Unknown script ${options.script}, pick one of ${scriptsText}.`);
91
+ (0, assert_1.guard)(target !== undefined, `Unknown script ${options.script}, pick one of ${getScriptsText()}.`);
85
92
  console.log(`Running script '${ansi_1.formatter.format(options.script, { style: 1 /* FontStyles.Bold */ })}'`);
86
93
  target = `cli/${target}`;
87
94
  log_1.log.debug(`Script maps to "${target}"`);
@@ -109,12 +116,13 @@ async function mainRepl() {
109
116
  // hook some handlers
110
117
  process.on('SIGINT', end);
111
118
  process.on('SIGTERM', end);
119
+ const allowRSessionAccess = options['r-session-access'] ?? false;
112
120
  if (options.execute) {
113
- await (0, core_1.replProcessAnswer)(main_1.standardReplOutput, options.execute, shell);
121
+ await (0, core_1.replProcessAnswer)(main_1.standardReplOutput, options.execute, shell, allowRSessionAccess);
114
122
  }
115
123
  else {
116
124
  await (0, print_version_1.printVersionRepl)(shell);
117
- await (0, core_1.repl)(shell);
125
+ await (0, core_1.repl)({ shell, allowRSessionAccess });
118
126
  }
119
127
  process.exit(0);
120
128
  }
@@ -130,7 +138,7 @@ async function mainServer(backend = new net_1.NetServer()) {
130
138
  // hook some handlers
131
139
  process.on('SIGINT', end);
132
140
  process.on('SIGTERM', end);
133
- await new server_1.FlowRServer(shell, backend).start(options.port);
141
+ await new server_1.FlowRServer(shell, options['r-session-access'], backend).start(options.port);
134
142
  }
135
143
  if (options.server) {
136
144
  void mainServer(options.ws ? new net_1.WebSocketServerWrapper() : new net_1.NetServer());
@@ -9,7 +9,7 @@ const normalize_meta_1 = require("../../../r-bridge/lang-4.x/ast/parser/xml/norm
9
9
  const default_pipelines_1 = require("../../../core/steps/pipeline/default-pipelines");
10
10
  const retriever_1 = require("../../../r-bridge/retriever");
11
11
  function toDepthMap(xml) {
12
- const root = (0, input_format_1.getKeysGuarded)(xml, "exprlist" /* RawRType.ExpressionList */);
12
+ const root = (0, input_format_1.getKeyGuarded)(xml, "exprlist" /* RawRType.ExpressionList */);
13
13
  const visit = [{ depth: 0, node: root }];
14
14
  const result = [];
15
15
  while (visit.length > 0) {
@@ -2,6 +2,7 @@
2
2
  import * as readline from 'readline';
3
3
  import type { ReplOutput } from './commands/main';
4
4
  import { RShell } from '../../r-bridge/shell';
5
+ import type { MergeableRecord } from '../../util/objects';
5
6
  /**
6
7
  * Used by the repl to provide automatic completions for a given (partial) input line
7
8
  */
@@ -10,11 +11,31 @@ export declare const DEFAULT_REPL_READLINE_CONFIGURATION: readline.ReadLineOptio
10
11
  /**
11
12
  * This function interprets the given `expr` as a REPL command (see {@link repl} for more on the semantics).
12
13
  *
13
- * @param output - Defines two methods that every function in the repl uses to output its data.
14
- * @param expr - The expression to process.
15
- * @param shell - The {@link RShell} to use (see {@link repl}).
14
+ * @param output - Defines two methods that every function in the repl uses to output its data.
15
+ * @param expr - The expression to process.
16
+ * @param shell - The {@link RShell} to use (see {@link repl}).
17
+ * @param allowRSessionAccess - If true, allows the execution of arbitrary R code.
16
18
  */
17
- export declare function replProcessAnswer(output: ReplOutput, expr: string, shell: RShell): Promise<void>;
19
+ export declare function replProcessAnswer(output: ReplOutput, expr: string, shell: RShell, allowRSessionAccess: boolean): Promise<void>;
20
+ /**
21
+ * Options for the {@link repl} function.
22
+ */
23
+ export interface FlowrReplOptions extends MergeableRecord {
24
+ /** The shell to use, if you do not pass one it will automatically create a new one with the `revive` option set to 'always'. */
25
+ readonly shell?: RShell;
26
+ /**
27
+ * A potentially customized readline interface to be used for the repl to *read* from the user, we write the output with the {@link ReplOutput | `output` } interface.
28
+ * If you want to provide a custom one but use the same `completer`, refer to {@link replCompleter}.
29
+ * For the default arguments, see {@link DEFAULT_REPL_READLINE_CONFIGURATION}.
30
+ */
31
+ readonly rl?: readline.Interface;
32
+ /** Defines two methods that every function in the repl uses to output its data. */
33
+ readonly output?: ReplOutput;
34
+ /** The file to use for persisting the repl's history. Passing undefined causes history not to be saved. */
35
+ readonly historyFile?: string;
36
+ /** If true, allows the execution of arbitrary R code. This is a security risk, as it allows the execution of arbitrary R code. */
37
+ readonly allowRSessionAccess?: boolean;
38
+ }
18
39
  /**
19
40
  * Provides a never-ending repl (read-evaluate-print loop) processor that can be used to interact with a {@link RShell} as well as all flowR scripts.
20
41
  *
@@ -22,15 +43,10 @@ export declare function replProcessAnswer(output: ReplOutput, expr: string, shel
22
43
  * - Starting with a colon `:`, indicating a command (probe `:help`, and refer to {@link commands}) </li>
23
44
  * - Starting with anything else, indicating default R code to be directly executed. If you kill the underlying shell, that is on you! </li>
24
45
  *
25
- * @param shell - The shell to use, if you do not pass one it will automatically create a new one with the `revive` option set to 'always'
26
- * @param rl - A potentially customized readline interface to be used for the repl to *read* from the user, we write the output with the {@link ReplOutput | `output` } interface.
27
- * If you want to provide a custom one but use the same `completer`, refer to {@link replCompleter}.
28
- * For the default arguments, see {@link DEFAULT_REPL_READLINE_CONFIGURATION}.
29
- * @param output - Defines two methods that every function in the repl uses to output its data.
30
- * @param historyFile - The file to use for persisting the repl's history. Passing undefined causes history not to be saved.
46
+ * @param options - The options for the repl. See {@link FlowrReplOptions} for more information.
31
47
  *
32
- * For the execution, this function makes use of {@link replProcessAnswer}
48
+ * For the execution, this function makes use of {@link replProcessAnswer}.
33
49
  *
34
50
  */
35
- export declare function repl(shell?: RShell, rl?: readline.Interface, output?: ReplOutput, historyFile?: string | undefined): Promise<void>;
51
+ export declare function repl({ shell, rl, output, historyFile, allowRSessionAccess }: FlowrReplOptions): Promise<void>;
36
52
  export declare function loadReplHistory(historyFile: string): string[] | undefined;
package/cli/repl/core.js CHANGED
@@ -94,37 +94,42 @@ exports.DEFAULT_REPL_READLINE_CONFIGURATION = {
94
94
  removeHistoryDuplicates: true,
95
95
  completer: replCompleter
96
96
  };
97
- async function replProcessStatement(output, statement, shell) {
97
+ async function replProcessStatement(output, statement, shell, allowRSessionAccess) {
98
98
  if (statement.startsWith(':')) {
99
99
  const command = statement.slice(1).split(' ')[0].toLowerCase();
100
100
  const processor = (0, commands_1.getCommand)(command);
101
+ const bold = (s) => output.formatter.format(s, { style: 1 /* FontStyles.Bold */ });
101
102
  if (processor) {
102
103
  try {
103
104
  await processor.fn(output, shell, statement.slice(command.length + 2).trim());
104
105
  }
105
106
  catch (e) {
106
- console.log(`${(0, ansi_1.bold)(`Failed to execute command ${command}`)}: ${e?.message}. Using the ${(0, ansi_1.bold)('--verbose')} flag on startup may provide additional information.`);
107
+ output.stdout(`${bold(`Failed to execute command ${command}`)}: ${e?.message}. Using the ${bold('--verbose')} flag on startup may provide additional information.\n`);
107
108
  }
108
109
  }
109
110
  else {
110
- console.log(`the command '${command}' is unknown, try ${(0, ansi_1.bold)(':help')} for more information`);
111
+ output.stdout(`the command '${command}' is unknown, try ${bold(':help')} for more information\n`);
111
112
  }
112
113
  }
113
- else {
114
+ else if (allowRSessionAccess) {
114
115
  await (0, execute_1.executeRShellCommand)(output, shell, statement);
115
116
  }
117
+ else {
118
+ output.stderr(`${output.formatter.format('You are not allowed to execute arbitrary R code.', { style: 1 /* FontStyles.Bold */, color: 1 /* Colors.Red */, effect: ansi_1.ColorEffect.Foreground })}\nIf you want to do so, please restart flowR with the ${output.formatter.format('--r-session-access', { style: 1 /* FontStyles.Bold */ })} flag. Please be careful of the security implications of this action.`);
119
+ }
116
120
  }
117
121
  /**
118
122
  * This function interprets the given `expr` as a REPL command (see {@link repl} for more on the semantics).
119
123
  *
120
- * @param output - Defines two methods that every function in the repl uses to output its data.
121
- * @param expr - The expression to process.
122
- * @param shell - The {@link RShell} to use (see {@link repl}).
124
+ * @param output - Defines two methods that every function in the repl uses to output its data.
125
+ * @param expr - The expression to process.
126
+ * @param shell - The {@link RShell} to use (see {@link repl}).
127
+ * @param allowRSessionAccess - If true, allows the execution of arbitrary R code.
123
128
  */
124
- async function replProcessAnswer(output, expr, shell) {
129
+ async function replProcessAnswer(output, expr, shell, allowRSessionAccess) {
125
130
  const statements = (0, args_1.splitAtEscapeSensitive)(expr, false, ';');
126
131
  for (const statement of statements) {
127
- await replProcessStatement(output, statement, shell);
132
+ await replProcessStatement(output, statement, shell, allowRSessionAccess);
128
133
  }
129
134
  }
130
135
  exports.replProcessAnswer = replProcessAnswer;
@@ -135,17 +140,12 @@ exports.replProcessAnswer = replProcessAnswer;
135
140
  * - Starting with a colon `:`, indicating a command (probe `:help`, and refer to {@link commands}) </li>
136
141
  * - Starting with anything else, indicating default R code to be directly executed. If you kill the underlying shell, that is on you! </li>
137
142
  *
138
- * @param shell - The shell to use, if you do not pass one it will automatically create a new one with the `revive` option set to 'always'
139
- * @param rl - A potentially customized readline interface to be used for the repl to *read* from the user, we write the output with the {@link ReplOutput | `output` } interface.
140
- * If you want to provide a custom one but use the same `completer`, refer to {@link replCompleter}.
141
- * For the default arguments, see {@link DEFAULT_REPL_READLINE_CONFIGURATION}.
142
- * @param output - Defines two methods that every function in the repl uses to output its data.
143
- * @param historyFile - The file to use for persisting the repl's history. Passing undefined causes history not to be saved.
143
+ * @param options - The options for the repl. See {@link FlowrReplOptions} for more information.
144
144
  *
145
- * For the execution, this function makes use of {@link replProcessAnswer}
145
+ * For the execution, this function makes use of {@link replProcessAnswer}.
146
146
  *
147
147
  */
148
- async function repl(shell = new shell_1.RShell({ revive: 2 /* RShellReviveOptions.Always */ }), rl = readline.createInterface(exports.DEFAULT_REPL_READLINE_CONFIGURATION), output = main_1.standardReplOutput, historyFile = defaultHistoryFile) {
148
+ async function repl({ shell = new shell_1.RShell({ revive: 2 /* RShellReviveOptions.Always */ }), rl = readline.createInterface(exports.DEFAULT_REPL_READLINE_CONFIGURATION), output = main_1.standardReplOutput, historyFile = defaultHistoryFile, allowRSessionAccess = false }) {
149
149
  if (historyFile) {
150
150
  rl.on('history', h => fs_1.default.writeFileSync(historyFile, h.join('\n'), { encoding: 'utf-8' }));
151
151
  }
@@ -155,7 +155,7 @@ async function repl(shell = new shell_1.RShell({ revive: 2 /* RShellReviveOption
155
155
  await new Promise((resolve, reject) => {
156
156
  rl.question((0, prompt_1.prompt)(), answer => {
157
157
  rl.pause();
158
- replProcessAnswer(output, answer, shell).then(() => {
158
+ replProcessAnswer(output, answer, shell, allowRSessionAccess).then(() => {
159
159
  rl.resume();
160
160
  resolve();
161
161
  }).catch(reject);
@@ -12,8 +12,9 @@ export declare class FlowRServerConnection {
12
12
  private readonly shell;
13
13
  private readonly name;
14
14
  private readonly logger;
15
+ private readonly allowRSessionAccess;
15
16
  private readonly fileMap;
16
- constructor(socket: Socket, name: string, shell: RShell);
17
+ constructor(socket: Socket, name: string, shell: RShell, allowRSessionAccess: boolean);
17
18
  private currentMessageBuffer;
18
19
  private handleData;
19
20
  private handleFileAnalysisRequest;