@eagleoutice/flowr 2.0.11 → 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.
- package/README.md +41 -8
- package/abstract-interpretation/domain.d.ts +33 -3
- package/abstract-interpretation/domain.js +31 -1
- package/benchmark/slicer.d.ts +3 -0
- package/benchmark/slicer.js +3 -0
- package/benchmark/stats/print.js +0 -1
- package/benchmark/summarizer/second-phase/graph.js +4 -4
- package/cli/flowr.d.ts +9 -8
- package/cli/flowr.js +22 -14
- package/cli/repl/core.d.ts +28 -12
- package/cli/repl/core.js +18 -18
- package/cli/repl/server/connection.d.ts +2 -1
- package/cli/repl/server/connection.js +61 -18
- package/cli/repl/server/messages/analysis.d.ts +11 -5
- package/cli/repl/server/messages/analysis.js +1 -1
- package/cli/repl/server/server.d.ts +2 -1
- package/cli/repl/server/server.js +4 -2
- package/core/steps/all/core/00-parse.d.ts +3 -3
- package/core/steps/all/core/00-parse.js +7 -1
- package/core/steps/all/core/20-dataflow.d.ts +2 -2
- package/core/steps/pipeline/default-pipelines.d.ts +8 -8
- package/dataflow/environments/built-in.js +1 -1
- package/dataflow/extractor.d.ts +2 -2
- package/dataflow/extractor.js +20 -4
- package/dataflow/info.d.ts +9 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-source.d.ts +8 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-source.js +25 -4
- package/dataflow/processor.d.ts +5 -4
- package/dataflow/processor.js +3 -2
- package/package.json +7 -5
- package/r-bridge/data/types.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/processing/decorate.d.ts +1 -0
- package/r-bridge/lang-4.x/ast/model/processing/decorate.js +5 -1
- package/r-bridge/lang-4.x/ast/parser/xml/internal/loops/normalize-repeat.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/parser/xml/internal/loops/normalize-repeat.js +2 -2
- package/r-bridge/retriever.d.ts +24 -16
- package/r-bridge/retriever.js +19 -8
- package/slicing/criterion/parse.js +4 -4
- package/util/version.js +1 -1
package/README.md
CHANGED
|
@@ -1,23 +1,56 @@
|
|
|
1
|
-
[](https://github.com/
|
|
1
|
+
[](https://github.com/flowr-analysis/flowr/wiki)\
|
|
2
|
+
[](https://github.com/flowr-analysis/flowr/actions/workflows/qa.yaml) [](https://codecov.io/gh/flowr-analysis/flowr) [](https://hub.docker.com/r/eagleoutice/flowr) [](https://github.com/flowr-analysis/flowr/releases/latest) [](https://marketplace.visualstudio.com/items?itemName=code-inspect.vscode-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/
|
|
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/
|
|
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/
|
|
19
|
-
|
|
20
|
-
|
|
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[] |
|
|
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
|
-
|
|
24
|
-
|
|
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) {
|
package/benchmark/slicer.d.ts
CHANGED
|
@@ -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.
|
package/benchmark/slicer.js
CHANGED
|
@@ -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).
|
package/benchmark/stats/print.js
CHANGED
|
@@ -49,10 +49,10 @@ function writeGraphOutput(ultimate, outputGraphPath) {
|
|
|
49
49
|
});
|
|
50
50
|
data.push({
|
|
51
51
|
name: 'memory (df-graph)',
|
|
52
|
-
unit: '
|
|
53
|
-
value:
|
|
54
|
-
range:
|
|
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
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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: '
|
|
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: '
|
|
35
|
-
{ name: '
|
|
36
|
-
{ name: 'r-
|
|
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 ${
|
|
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());
|
package/cli/repl/core.d.ts
CHANGED
|
@@ -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
|
|
14
|
-
* @param expr
|
|
15
|
-
* @param shell
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
121
|
-
* @param expr
|
|
122
|
-
* @param shell
|
|
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
|
|
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;
|